Repository: garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate
Branch: master
Commit: 54cfdec9469f
Files: 89
Total size: 95.2 KB
Directory structure:
gitextract_51xeknsa/
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── Express_GraphQL_Apollo_Mongodb_Server/
│ ├── .gitignore
│ ├── config/
│ │ ├── express.ts
│ │ └── index.ts
│ ├── index.ts
│ ├── package.json
│ ├── server/
│ │ ├── graphql/
│ │ │ ├── resolvers/
│ │ │ │ ├── index.ts
│ │ │ │ ├── merge.ts
│ │ │ │ └── user.ts
│ │ │ └── schema/
│ │ │ └── index.ts
│ │ ├── helpers/
│ │ │ └── date.ts
│ │ ├── middleware/
│ │ │ └── auth.ts
│ │ └── models/
│ │ └── user.ts
│ ├── tsconfig.json
│ └── tslint.json
├── LICENSE
├── README.md
├── nextjs-app/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc.js
│ ├── next-env.d.ts
│ ├── package.json
│ ├── pages/
│ │ ├── _app.tsx
│ │ ├── index.tsx
│ │ ├── signup.tsx
│ │ ├── subscription.tsx
│ │ ├── update.tsx
│ │ ├── users.tsx
│ │ └── welcome.tsx
│ ├── src/
│ │ ├── components/
│ │ │ ├── Card.tsx
│ │ │ ├── Footer.tsx
│ │ │ ├── List.tsx
│ │ │ └── Login.tsx
│ │ ├── config/
│ │ │ └── index.ts
│ │ ├── configureClient.ts
│ │ ├── graphql/
│ │ │ ├── mutation/
│ │ │ │ ├── createUser.ts
│ │ │ │ └── updateUser.ts
│ │ │ ├── query/
│ │ │ │ ├── login.ts
│ │ │ │ └── user.ts
│ │ │ └── subscription/
│ │ │ └── users.ts
│ │ └── utils/
│ │ ├── auth.tsx
│ │ └── validation.ts
│ └── tsconfig.json
└── react-app/
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── package.json
├── public/
│ ├── index.html
│ └── manifest.json
├── src/
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components/
│ │ ├── Card/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Footer/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── List/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ └── LoginForm/
│ │ ├── index.tsx
│ │ └── styles.scss
│ ├── config/
│ │ └── index.ts
│ ├── configureClient.ts
│ ├── graphql/
│ │ ├── mutation/
│ │ │ ├── createUser.ts
│ │ │ └── updateUser.ts
│ │ ├── query/
│ │ │ ├── login.ts
│ │ │ └── user.ts
│ │ └── subscription/
│ │ └── users.ts
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── screens/
│ │ ├── Login/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── NoMatch/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── SignUp/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Subscription/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Update/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Users/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ └── Welcome/
│ │ ├── index.tsx
│ │ └── styles.scss
│ ├── setupTests.ts
│ └── utils/
│ ├── auth.tsx
│ └── validation.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
custom: ['https://www.buymeacoffee.com/anuraggarg']
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/.gitignore
================================================
node_modules
yarn.lock
dist
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/config/express.ts
================================================
/**
* File containing Express Configuration
* @author Anurag Garg <garganurag893@gmail.com>
*/
import { ApolloServer } from 'apollo-server-express';
import cors from 'cors';
import express from 'express';
import * as http from 'http';
import schema from '../server/graphql/schema/index';
import auth from '../server/middleware/auth';
import config from './index';
class Express {
public express: express.Application;
public server: ApolloServer = new ApolloServer(schema);
public httpServer: http.Server;
public init = (): void => {
/**
* Creating an express application
*/
this.express = express();
/**
* Middlerware for using CORS
*/
this.express.use(cors({
origin(origin, callback) {
/**
* Allow requests with no origin
* Like mobile apps or curl requests
*/
if (!origin) { return callback(null, true); }
if (config.allowedOrigins.indexOf(origin) === -1) {
const msg = `The CORS policy for this site does not
allow access from the specified Origin.`;
return callback(new Error(msg), false);
}
return callback(null, true);
}
}));
/**
* Middlerware for extracting authToken
*/
this.express.use(auth);
this.server.applyMiddleware({ app: this.express });
this.httpServer = http.createServer(this.express);
/**
* Installing subscription handlers
*/
this.server.installSubscriptionHandlers(this.httpServer);
}
}
export default Express;
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/config/index.ts
================================================
/**
* Config file
* @author Anurag Garg <garganurag893@gmail.com>
*/
import dotenv from 'dotenv';
dotenv.config();
export default {
db: process.env.DB,
jwtSecret: process.env.JWT_SECRET,
port: process.env.PORT,
allowedOrigins: ['http://localhost:3000', 'http://yourapp.com', 'http://localhost:4020']
};
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/index.ts
================================================
/**
* Bootstrap your app
* @author Anurag Garg <garganurag893@gmail.com>
*/
import Promise from 'bluebird';
import mongoose from 'mongoose';
import config from './config';
import Express from './config/express';
/**
* Promisify All The Mongoose
* @param mongoose
*/
Promise.promisifyAll(mongoose);
/**
* Connecting Mongoose
* @param uris
* @param options
*/
mongoose.connect(config.db, {
bufferMaxEntries: 0,
keepAlive: true,
reconnectInterval: 500,
reconnectTries: 30,
socketTimeoutMS: 0,
useNewUrlParser: true,
useUnifiedTopology: true
});
/**
* Throw error when not able to connect to database
*/
mongoose.connection.on('error', () => {
throw new Error(`unable to connect to database: ${config.db}`);
});
/**
* Initialize Express
*/
const ExpressServer = new Express();
ExpressServer.init();
/**
* Listen to port
*/
ExpressServer.httpServer.listen(process.env.PORT || config.port, () => {
console.log(`🚀 Server ready at ${config.port}`);
console.log(
`🚀 Server ready at http://localhost:${config.port}${ExpressServer.server.graphqlPath}`
);
console.log(
`🚀 Subscriptions ready at ws://localhost:${config.port}${ExpressServer.server.subscriptionsPath}`
);
});
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/package.json
================================================
{
"name": "Express_GraphQL_Apollo_Mongodb_Server",
"version": "1.0.0",
"description": "Backend Server for Next.js GraphQL Express Apollo Boilerplate",
"private": true,
"main": "dist/index.js",
"scripts": {
"clean": "rm -rf dist",
"prebuild": "tslint -c tslint.json -p tsconfig.json --fix",
"build": "yarn clean && tsc",
"prestart": "yarn build",
"start": "tsc --watch & nodemon dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1",
"pre-commit": "yarn tslint && yarn build",
"tslint": "tslint --project tsconfig.json",
"tslint:fix": "tslint --project tsconfig.json --fix"
},
"keywords": [],
"author": "Anurag Garg",
"license": "MIT",
"husky": {
"hooks": {
"pre-commit": "yarn pre-commit"
}
},
"dependencies": {
"apollo-server": "^2.9.6",
"apollo-server-express": "^2.9.6",
"bluebird": "^3.5.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.15",
"mongoose": "^5.6.13"
},
"devDependencies": {
"@types/bluebird": "^3.5.29",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.2",
"@types/jsonwebtoken": "^8.3.6",
"@types/lodash": "^4.14.149",
"@types/mongoose": "^5.5.41",
"@types/node": "^13.1.7",
"husky": "^4.2.0",
"nodemon": "^1.19.2",
"tslint": "^5.20.1",
"typescript": "^3.7.5"
}
}
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/index.ts
================================================
/**
* Exporting all resolvers
* @author Anurag Garg <garganurag893@gmail.com>
*/
import { UserMutation, UserQueries, UserSubscription } from './user';
const rootResolver = {
Query: {
...UserQueries
// Add other queries here
},
Mutation: {
...UserMutation
// Add other mutations here
},
Subscription: {
...UserSubscription
// Add other subscriptions here
}
};
export default rootResolver;
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/merge.ts
================================================
/**
* Primary file for extracting proper schema structured objects
* @author Anurag Garg <garganurag893@gmail.com>
*/
import dateToString from '../../helpers/date';
import User from '../../models/user';
/**
* Get user object with schema typing
* @param id
*/
const getUser = async (id: string) => {
try {
const user: any = await User.findById(id);
return {
...user._doc,
_id: user.id,
createdAt: dateToString(user._doc.createdAt),
updatedAt: dateToString(user._doc.updatedAt)
};
} catch (err) {
throw err;
}
};
/**
* Get user object with schema typing
* @param user
*/
const transformUser = (user: any) => {
return {
...user._doc,
_id: user.id,
createdAt: dateToString(user._doc.createdAt),
updatedAt: dateToString(user._doc.updatedAt)
};
};
export { getUser, transformUser };
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/user.ts
================================================
/**
* File containing all user queries, mutations and subscriptions
* @author Anurag Garg <garganurag893@gmail.com>
*/
import { PubSub } from 'apollo-server';
import jwt from 'jsonwebtoken';
import mongoose from 'mongoose';
import config from '../../../config';
import User from '../../models/user';
import { transformUser } from './merge';
const pubsub = new PubSub();
const USER_ADDED = 'USER_ADDED';
/**
* User Queries
*/
const UserQueries = {
users: async (parent, args, context) => {
try {
const users = await User.find();
return users.map((user) => {
return transformUser(user);
});
} catch (err) {
throw err;
}
},
user: async (parent, { userId }) => {
try {
const user = await User.findById(userId);
return transformUser(user);
} catch (err) {
throw err;
}
},
login: async (parent, { email, password }) => {
try {
const user: any = await User.findOne({ email, password });
if (!user) {
throw new Error('User does not Exists');
}
const token = jwt.sign({ userId: user.id }, config.jwtSecret, {
expiresIn: '1h'
});
return {
userId: user.id,
token,
tokenExpiration: 1
};
} catch (err) {
throw err;
}
}
};
/**
* User Mutations
*/
const UserMutation = {
createUser: async (parent: any, { userInput }: any) => {
try {
const user = await User.findOne({
email: userInput.email
});
if (user) {
throw new Error('User already Exists');
} else {
const newUser = new User({
_id: new mongoose.Types.ObjectId(),
email: userInput.email,
name: userInput.name,
password: userInput.password
});
const savedUser = await newUser.save();
pubsub.publish(USER_ADDED, {
userAdded: transformUser(savedUser)
});
const token = jwt.sign({ userId: savedUser.id }, config.jwtSecret, {
expiresIn: '1h'
});
return {
userId: savedUser.id,
token,
tokenExpiration: 1
};
}
} catch (error) {
throw error;
}
},
updateUser: async (parent, { userId, updateUser }, context) => {
// If not authenticated throw error
if (!context.isAuth) {
throw new Error('Non Authenticated');
}
try {
const user = await User.findByIdAndUpdate(userId, updateUser, {
new: true
});
return transformUser(user);
} catch (error) {
throw error;
}
}
};
/**
* User Subscriptions
*/
const UserSubscription = {
userAdded: {
subscribe: () => pubsub.asyncIterator([USER_ADDED])
}
};
export { UserQueries, UserMutation, UserSubscription };
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/graphql/schema/index.ts
================================================
/**
* Primary file for GraphQL Schema
* @author Anurag Garg <garganurag893@gmail.com>
*/
import { gql } from 'apollo-server-express';
import { ApolloServerExpressConfig } from 'apollo-server-express';
import resolvers from '../resolvers/index';
const typeDefs = gql`
type Query {
users: [User!]!
user(userId: ID!): User!
login(email: String!, password: String!): AuthData!
}
type Mutation {
createUser(userInput: UserInput): AuthData!
updateUser(userId: ID!, updateUser: UpdateUser): User!
}
type Subscription {
userAdded: User
}
type User {
_id: ID!
email: String!
name: String!
password: String
createdAt: String!
updatedAt: String!
}
type AuthData {
userId: ID!
token: String!
tokenExpiration: Int!
}
input UserInput {
email: String!
name: String!
password: String!
}
input UpdateUser {
email: String
name: String
password: String
}
`;
const schema: ApolloServerExpressConfig = {
typeDefs,
resolvers,
introspection: true,
context: async ({ req, connection, payload }: any) => {
if (connection) {
return { isAuth: payload.authToken };
}
return { isAuth: req.isAuth };
},
playground: true
};
export default schema;
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/helpers/date.ts
================================================
/**
* Define nethod for converting date to string
* @author Anurag Garg <garganurag893@gmail.com>
*/
const dateToString = (date: Date) => new Date(date).toISOString();
export default dateToString;
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/middleware/auth.ts
================================================
/**
* Define middlerware for extracting authToken
* @author Anurag Garg <garganurag893@gmail.com>
*/
import * as jwt from 'jsonwebtoken';
import config from '../../config';
export default (req: any, res: any, next: any) => {
const authHeader = req.get('Authorization');
if (!authHeader) {
req.isAuth = false;
return next();
}
const token = authHeader.split(' ')[1];
if (!token || token === '') {
req.isAuth = false;
return next();
}
let decodedToken: any;
try {
decodedToken = jwt.verify(token, config.jwtSecret);
} catch (err) {
req.isAuth = false;
return next();
}
if (!decodedToken) {
req.isAuth = false;
return next();
}
req.isAuth = true;
req.userId = decodedToken.userId;
return next();
};
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/models/user.ts
================================================
/**
* Define model for user
* @author Anurag Garg <garganurag893@gmail.com>
*/
import mongoose from 'mongoose';
/**
* User Schema
*/
const userSchema = new mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
email: {
type: String,
required: true
},
name: {
type: String,
required: true
},
password: {
type: String
}
},
{
timestamps: true
}
);
/**
* Statics
*/
userSchema.statics = {
/**
* Get User
* @param {ObjectId} id - The objectId of user.
*/
get(id: string): mongoose.Document {
return this.findById(id)
.execAsync()
.then((user: any) => {
if (user) {
return user;
}
});
}
};
export default mongoose.model('User', userSchema);
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"pretty": true,
"sourceMap": true,
"outDir": "dist",
"importHelpers": true,
"strict": true,
"noImplicitAny": false,
"strictNullChecks": false,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"baseUrl": ".",
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": [
"es5",
"es6",
"dom",
"es2015.core",
"es2015.collection",
"es2015.generator",
"es2015.iterable",
"es2015.promise",
"es2015.proxy",
"es2015.reflect",
"es2015.symbol",
"es2015.symbol.wellknown",
"esnext.asynciterable"
]
},
"include": ["server/**/*", "config/**/*", "index.ts"]
}
================================================
FILE: Express_GraphQL_Apollo_Mongodb_Server/tslint.json
================================================
{
"defaultSeverity": "error",
"extends": [
"tslint:recommended"
],
"rules": {
"no-trailing-whitespace": [true, "always"],
"prefer-const": [true, {
"destructuring": "any"
}],
"semicolon": [true, "always"],
"quotemark": [true, "single"],
"trailing-comma": [
true,
{
"singleline": "never",
"multiline": "never"
}
],
"object-literal-sort-keys": false,
"no-console": false
},
"linterOptions": {
"exclude": [
"node_modules/**",
"dist/**/*.ts"
]
},
"rulesDirectory": []
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Anurag Garg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<a href="https://twitter.com/AnuragG94634191"><img src="./Gifs/boilerplate.gif" title="Anurag Garg" alt="Anurag Garg"></a>
</p>
<h1 align="center">Next.js React GraphQL Express Apollo Boilerplate</h1>
<p align="center">Performance oriented Next.js
and React.js application boilerplate with Typescript, Express.js, GraphQL, Apollo and Sass
</p>
<p align="center">
<img src="http://img.shields.io/travis/badges/badgerbadgerbadger.svg?style=flat-square" alt="build"/>
<img src="https://img.shields.io/github/issues/garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate" alt="build"/>
<img src="https://img.shields.io/github/issues-pr/garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate" alt="build"/>
<img src="http://img.shields.io/:license-mit-blue.svg?style=flat-square" alt="build"/>
</p>
## Table of Contents
- [Table of Contents](#table-of-contents)
- [Installation](#installation)
- [Step 1: Set up the Development Environment](#step-1-set-up-the-development-environment)
- [Step 2: Set up Env](#step-2-set-up-env)
- [Step 3: Install dependencies](#step-3-install-dependencies)
- [Step 4: Running Locally](#step-4-running-locally)
- [Step 5: Deployment](#step-5-deployment)
- [Features](#features)
- [GraphQL](#graphql)
- [Express](#express)
- [Next.js](#nextjs)
- [React](#react)
- [React Apollo](#react-apollo)
- [Typescript](#typescript)
- [JsonWebToken](#jsonwebtoken)
- [TSLint](#tslint)
- [Husky](#husky)
- [Bluebird](#bluebird)
- [Cors](#cors)
- [Contributing](#contributing)
- [Step 1](#step-1)
- [Step 2](#step-2)
- [Step 3](#step-3)
- [Support](#support)
- [Donations](#donations)
- [License](#license)
## Installation
Clone this repo to your local machine using `https://github.com/garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate`
### Step 1: Set up the Development Environment
You need to set up your development environment before you can do anything.
**Install [Node.js and NPM](https://nodejs.org/en/download/)**
- on OSX use [homebrew](http://brew.sh) `brew install node`
- on Windows use [chocolatey](https://chocolatey.org/) `choco install nodejs`
**Install yarn globally**
```bash
yarn global add yarn
```
> NOTE : If you work with a mac, we recommend to use homebrew for the installation.
**Install MongoDB**
Once Brew is installed, it is time to install MongoDB by issuing the following command on the Terminal:
```bash
brew install mongodb
```
### Step 2: Set up Env
Open .env file in a editor and add your configuration for database and other required fields.
```ts
NODE_ENV = development;
JWT_SECRET = "somesuperkey";
DB =
"mongodb://localhost/nextjs_graphql_express_apollo_boilerplate_development";
PORT = 4020;
```
### Step 3: Install dependencies
Navigate to the server, nextjs and react app directories and run the below command:
```bash
$ yarn
```
### Step 4: Running Locally
Navigate to the **Express Server** directory and run the below command in your terminal :
```bash
$ yarn start
```
Now navigate to **Nextjs App** directory and run the below command in your terminal :
```bash
$ yarn dev
```
Now navigate to **React App** directory and run the below command in your terminal :
```bash
$ yarn start
```
### Step 5: Deployment
To deploy with ZEIT Now through your terminal, you will need to install Now CLI, a frequently updated, and open-source, command-line interface.
You can get Now CLI from either npm or Yarn. Using npm, run the following command from your terminal:
```bash
$ npm i -g now
```
To verify that you have installed Now CLI, try running now help from your terminal.
With Now CLI installed, you can now login using the following command:
```bash
$ now login
```
Navigate to **Nextjs App** directory and run the below commands in order :
```bash
$ now
```
Once deployed, you will get a preview URL that is assigned on each deployment to share the latest changes under the same address.
## Features
### GraphQL
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.
### Express
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
### Next.js
Next.js extends React to provide a powerful method for loading a page's initial data, no matter where it is coming from. With a single place to prepopulate page context, server-side rendering with Next.js seamlessly integrates with any existing data-fetching strategy.
### React
React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.
### React Apollo
React Apollo allows you to fetch data from your GraphQL server and use it in building complex and reactive UIs using the React framework. React Apollo may be used in any context that React may be used. In the browser, in React Native, or in Node.js when you want to do server-side rendering.
### Typescript
TypeScript is an open-source programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript, and adds optional static typing to the language. TypeScript is designed for development of large applications and transcompiles to JavaScript.
### JsonWebToken
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.
### TSLint
TSLint is an extensible static analysis tool that checks TypeScript code for readability, maintainability, and functionality errors
### Husky
Husky can prevent bad git commit, git push and more 🐶 woof!
### Bluebird
Bluebird is a fully featured promise library with focus on innovative features and performance.
### Cors
Cross-origin resource sharing is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served
## Contributing
> To get started...
### Step 1
- **Option 1**
- 🍴 Fork this repo!
- **Option 2**
- 👯 Clone this repo to your local machine using `https://github.com/garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate`
### Step 2
- **HACK AWAY!** 🔨🔨🔨
### Step 3
- 🔃 Create a new pull request using <a href="https://github.com/garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate" target="_blank">`https://github.com/garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate`</a>.
## Support
Reach out to me at one of the following places!
- Twitter at <a href="https://twitter.com/AnuragG94634191" target="_blank">https://twitter.com/AnuragG94634191</a>
- Medium at <a href="https://medium.com/@garganurag893" target="_blank">https://medium.com/@garganurag893</a>
- Instagram at <a href="https://www.instagram.com/the_only_anurag/" target="_blank">https://www.instagram.com/the_only_anurag/</a>
- Email at garganurag893@gmail.com
## Donations
If this boilerplate help save your valuable time and you feel to help me donate now to help me create more amazing stuff.
[](https://paypal.me/garganurag893?locale.x=en_GB)
<p>
<img src="https://www.komando.com/wp-content/uploads/2019/05/google-pay-badge.png" alt="Support via GooglePay" height="50"/>
</p>
<h5>+919468026011
</h5>
## License
[](http://badges.mit-license.org)
- **[MIT license](http://opensource.org/licenses/mit-license.php)**
- Copyright 2020 © <a href="https://twitter.com/AnuragG94634191" target="_blank">Anurag Garg</a>.
================================================
FILE: nextjs-app/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'react-app',
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'react'],
rules: {
"@typescript-eslint/explicit-function-return-type":0,
"@typescript-eslint/no-explicit-any":0,
},
};
================================================
FILE: nextjs-app/.gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
.env*
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: nextjs-app/.prettierrc.js
================================================
module.exports = {
useTabs: false,
printWidth: 80,
singleQuote: true,
trailingComma: 'es5',
jsxBracketSameLine: true,
noSemi: false
};
================================================
FILE: nextjs-app/next-env.d.ts
================================================
/// <reference types="next" />
/// <reference types="next/types/global" />
================================================
FILE: nextjs-app/package.json
================================================
{
"name": "nextjs-graphql-apollo-client",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint './**/*.{ts,js,tsx}'"
},
"author": "Anurag Garg",
"license": "MIT",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"./**/*.{js,jsx,ts,tsx,css,scss}": [
"prettier --write",
"yarn lint"
]
},
"dependencies": {
"@apollo/react-components": "^3.1.3",
"@apollo/react-hooks": "^3.1.3",
"apollo-cache-inmemory": "^1.6.5",
"apollo-client": "^2.6.8",
"apollo-link-batch-http": "^1.2.13",
"apollo-link-http": "^1.5.16",
"apollo-link-ws": "^1.0.19",
"graphql": "^14.5.8",
"graphql-tag": "^2.10.1",
"isomorphic-unfetch": "^3.0.0",
"js-cookie": "^2.2.1",
"next": "9.2.0",
"next-cookies": "^2.0.3",
"next-with-apollo": "^4.3.0",
"react": "16.12.0",
"react-apollo": "^3.1.3",
"react-dom": "16.12.0",
"react-toastify": "^5.5.0",
"subscriptions-transport-ws": "^0.9.16"
},
"devDependencies": {
"@types/next": "^9.0.0",
"@types/node": "^13.1.8",
"@types/react": "^16.9.17",
"@typescript-eslint/eslint-plugin": "^2.16.0",
"@typescript-eslint/parser": "^2.16.0",
"babel-eslint": "^10.0.3",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"eslint-config-react-app": "^5.1.0",
"eslint-plugin-flowtype": "^4",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.18.0",
"eslint-plugin-react-hooks": "^2.3.0",
"husky": "^4.2.0",
"lint-staged": "^10.0.2",
"prettier": "^1.19.1",
"typescript": "^3.7.5"
}
}
================================================
FILE: nextjs-app/pages/_app.tsx
================================================
/**
* App Configuration
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import App from 'next/app';
import { ApolloProvider } from '@apollo/react-hooks';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import withData from '../src/configureClient';
class MyApp extends App<any> {
render() {
const { Component, pageProps, apollo } = this.props;
return (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
<ToastContainer
position="top-right"
autoClose={2000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
draggable
pauseOnHover
/>
</ApolloProvider>
);
}
}
// Wraps all components in the tree with the data provider
export default withData(MyApp);
================================================
FILE: nextjs-app/pages/index.tsx
================================================
/**
* Main Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Login from '../src/components/Login';
import Footer from '../src/components/Footer';
const Home: React.SFC = () => {
return (
<div className="mainContainer">
<div className="container">
<h1 className="heading">Welcome</h1>
<Login />
</div>
<Footer />
<style jsx>
{`
.mainContainer {
height: 100%;
min-height: 100vh;
width: 100%;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-flow: row wrap;
padding: 8rem 0rem !important;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 2rem;
}
@media only screen and (max-width: 740px) {
.container {
padding: 3rem 0rem !important;
}
}
`}
</style>
<style jsx global>
{`
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
`}
</style>
</div>
);
};
export default Home;
================================================
FILE: nextjs-app/pages/signup.tsx
================================================
/**
* SignUp Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Router from 'next/router';
import { toast } from 'react-toastify';
import Footer from '../src/components/Footer';
import Cookies from 'js-cookie';
import { Mutation } from '@apollo/react-components';
import CREATE_USER from '../src/graphql/mutation/createUser';
import { setToken } from '../src/configureClient';
import { validateEmail } from '../src/utils/validation';
interface SignUpState {
[key: string]: any;
name: string;
email: string;
password: string;
}
class SignUp extends React.PureComponent<any, SignUpState> {
constructor(props) {
super(props);
this.state = {
name: '',
email: '',
password: '',
};
}
handleChange = (event: any) => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = async (createUser, event) => {
try {
event.preventDefault();
const { state } = this;
if (validateEmail(state.email)) {
const data = await createUser({
variables: {
userInput: { ...state },
},
});
const { token, userId } = data.createUser;
setToken(token);
Cookies.set('userId', userId, { expires: 7 });
Router.replace('/welcome');
} else {
toast.error('Invalid Email');
}
} catch (error) {
toast.error('Check your connection');
}
};
render() {
const { state } = this;
return (
<div className="container">
<h1 className="heading">Sign Up</h1>
<Mutation mutation={CREATE_USER}>
{(createUser, { loading, error }) => (
<form
onSubmit={event => this.handleSubmit(createUser, event)}
className="signup-form">
<input
type="text"
placeholder="Name"
name="name"
value={state.name}
onChange={this.handleChange}
className="signup-input-box"
required
/>
<input
type="text"
placeholder="Email"
name="email"
value={state.email}
onChange={this.handleChange}
className="signup-input-box"
required
/>
<input
type="password"
placeholder="Password"
name="password"
value={state.password}
onChange={this.handleChange}
className="signup-input-box"
required
/>
<input type="submit" value="Submit" className="signup-button" />
</form>
)}
</Mutation>
<Footer />
<style jsx>
{`
.container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 5rem 0rem;
}
form {
display: flex;
flex-flow: column wrap;
justify-content: center;
align-items: center;
}
.signup-input-box {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
border: 0;
width: 15rem;
padding: 0.5rem;
height: 2rem;
margin-bottom: 2rem;
font-family: Candara;
}
.signup-button {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
width: 15rem;
font-size: 0.9rem;
padding: 0.5rem;
height: 3rem;
margin: 2rem;
font-family: Candara;
transition: transform 0.2s;
cursor: pointer;
}
.signup-button:hover {
transform: scale(1.1);
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
color: white;
}
input:focus {
outline: none;
}
`}
</style>
<style jsx global>
{`
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
`}
</style>
</div>
);
}
}
export default SignUp;
================================================
FILE: nextjs-app/pages/subscription.tsx
================================================
/**
* Subscription Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { useSubscription } from '@apollo/react-hooks';
import { UserCard } from '../src/components/Card';
import USER_ADDED from '../src/graphql/subscription/users';
import { withAuthSync } from '../src/utils/auth';
const Subscription = () => {
const { data, loading, error } = useSubscription(USER_ADDED);
let message = 'New User';
if (loading) message = 'Listening...';
if (error) message = `Error! ${error.message}`;
if (data && data.userAdded.length <= 0) message = 'No New User Added';
return (
<div className="container">
<h1 className="heading">{message}</h1>
{data && data.userAdded && (
<div className="listContainer">
<UserCard img="./user.png" user={data.userAdded} />
</div>
)}
<style jsx>
{`
.container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.listContainer {
display: flex;
flex-flow: wrap row;
justify-content: space-evenly;
align-items: center;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 0rem 0 5rem;
}
`}
</style>
<style jsx global>
{`
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
`}
</style>
</div>
);
};
export default withAuthSync(Subscription);
================================================
FILE: nextjs-app/pages/update.tsx
================================================
/**
* Update Profile Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Router from 'next/router';
import { toast } from 'react-toastify';
import Footer from '../src/components/Footer';
import { Mutation } from '@apollo/react-components';
import { validateEmail } from '../src/utils/validation';
import UPDATE_USER from '../src/graphql/mutation/updateUser';
import { withAuthSync } from '../src/utils/auth';
interface UpdateState {
[key: string]: any;
name: string;
email: string;
password: string;
}
class Update extends React.PureComponent<any, UpdateState> {
constructor(props) {
super(props);
this.state = {
name: '',
email: '',
password: '',
};
}
handleChange = (event: any) => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = async (updateUser, event) => {
try {
event.preventDefault();
const { state, props } = this;
if (validateEmail(state.email)) {
await updateUser({
variables: {
userId: props.userId,
updateUser: { ...state },
},
});
toast.success('Profile Updated');
Router.push('/welcome');
} else {
toast.error('Invalid Email');
}
} catch (error) {
toast.error('Check your connection');
}
};
render() {
const { state } = this;
return (
<div className="container">
<h1 className="heading">Update Profile</h1>
<Mutation mutation={UPDATE_USER}>
{updateUser => (
<form
onSubmit={event => this.handleSubmit(updateUser, event)}
className="update-form">
<input
type="text"
placeholder="Name"
name="name"
value={state.name}
onChange={this.handleChange}
className="update-input-box"
required
/>
<input
type="text"
placeholder="Email"
name="email"
value={state.email}
onChange={this.handleChange}
className="update-input-box"
required
/>
<input
type="password"
placeholder="Password"
name="password"
value={state.password}
onChange={this.handleChange}
className="update-input-box"
required
/>
<input type="submit" value="Submit" className="update-button" />
</form>
)}
</Mutation>
<Footer />
<style jsx>
{`
.container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 5rem 0rem;
}
form {
display: flex;
flex-flow: column wrap;
justify-content: center;
align-items: center;
}
.update-input-box {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
border: 0;
width: 15rem;
padding: 0.5rem;
height: 2rem;
margin-bottom: 2rem;
font-family: Candara;
}
.update-button {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
width: 15rem;
font-size: 0.9rem;
padding: 0.5rem;
height: 3rem;
margin: 2rem;
font-family: Candara;
transition: transform 0.2s;
cursor: pointer;
}
.update-button:hover {
transform: scale(1.1);
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
color: white;
}
input:focus {
outline: none;
}
`}
</style>
<style jsx global>
{`
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
`}
</style>
</div>
);
}
}
export default withAuthSync(Update);
================================================
FILE: nextjs-app/pages/users.tsx
================================================
/**
* User List Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import List from '../src/components/List';
import GET_USERS from '../src/graphql/query/user';
import { withAuthSync } from '../src/utils/auth';
interface User {
name: string;
_id: string;
email: string;
}
interface Data {
users: [User];
}
interface UserProps {
loading: boolean;
data: Data;
error: string;
}
const Users = (props: UserProps) => {
const { loading, error, data } = props;
let message = 'Users';
if (loading) message = 'Loading...';
if (error) message = `Error! ${error}`;
if (data && data.users.length <= 0) message = 'No Users';
return (
<div className="container">
<h1 className="heading">{message}</h1>
{data && data.users.length > 0 && (
<div className="listContainer">
<List img="./user.png" data={data.users} />
</div>
)}
<style jsx>
{`
.container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.listContainer {
display: flex;
flex-flow: wrap row;
justify-content: space-evenly;
align-items: center;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 0rem 0 5rem;
}
`}
</style>
<style jsx global>
{`
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
`}
</style>
</div>
);
};
Users.getInitialProps = async ctx => {
try {
const { data, loading } = await ctx.apolloClient.query({
query: GET_USERS,
});
return { data, loading };
} catch (error) {
return {
error: 'Failed to fetch',
};
}
};
export default withAuthSync(Users);
================================================
FILE: nextjs-app/pages/welcome.tsx
================================================
/**
* Welcome Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Card from '../src/components/Card';
import Footer from '../src/components/Footer';
import { withAuthSync } from '../src/utils/auth';
const Welcome: React.SFC = () => {
return (
<div className="container">
<h1 className="heading">Welcome</h1>
<div className="cardContainer">
<Card title="Query" href="/users" />
<Card title="Mutation" href="/update" />
<Card title="Subscription" href="/subscription" />
</div>
<Footer />
<style jsx>
{`
.container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.cardContainer {
display: flex;
flex-flow: wrap row;
justify-content: center;
align-items: center;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 6rem 0rem;
}
`}
</style>
<style jsx global>
{`
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
`}
</style>
</div>
);
};
export default withAuthSync(Welcome);
================================================
FILE: nextjs-app/src/components/Card.tsx
================================================
/**
* Card Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Link from 'next/link';
interface User {
name: string;
_id: string;
email: string;
}
interface CardProps {
title: string;
href: string;
}
interface UserCardProps {
user: User;
img: string;
}
const Card: React.SFC<CardProps> = props => {
return (
<Link href={props.href}>
<div className="card">
<h2>{props.title}</h2>
<style jsx>
{`
.card {
display: flex;
height: 5rem;
width: 20rem;
justify-content: center;
padding: 2rem;
align-self: stretch;
align-items: center;
background-color: white;
margin: 2rem;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
transition: transform 0.2s;
cursor: pointer;
}
.card:hover {
transform: scale(1.1);
}
h2 {
font-size: 2rem;
}
`}
</style>
</div>
</Link>
);
};
export const UserCard: React.SFC<UserCardProps> = props => {
return (
<div key={props.user._id} className="listCard">
<img src={props.img} alt="user" height="90" />
<div className="userCardDetails">
<h2>{props.user.name}</h2>
<h2>{props.user.email}</h2>
</div>
<style jsx>
{`
.listCard {
display: flex;
flex-flow: wrap row;
justify-content: space-between;
padding: 2rem;
-webkit-align-self: stretch;
align-self: stretch;
align-items: center;
background-color: white;
margin: 2rem;
width: 20rem;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
transition: transform 0.2s;
}
.userCardDetails {
display: flex;
flex-flow: wrap column;
justify-content: space-between;
align-items: flex-end;
height: 3rem;
}
h2 {
font-size: 1rem;
text-align: right;
}
img {
margin-right: 2rem;
}
`}
</style>
</div>
);
};
export default Card;
================================================
FILE: nextjs-app/src/components/Footer.tsx
================================================
/**
* Footer Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
const Footer: React.SFC<any> = props => {
return (
<footer className="footer">
<div className="footer-text-container">
<p>
© Copyright 2020{' '}
<a href="https://twitter.com/AnuragG94634191">Anurag Garg</a>
</p>
</div>
<style jsx>
{`
.footer {
color: rgba(255, 255, 255, 0.5);
margin-top: auto !important;
}
.footer-text-container {
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
text-shadow: 0 0.05rem 0.1rem rgba(0, 0, 0, 0.5);
text-align: center !important;
color: rgba(255, 255, 255, 0.5);
}
a {
text-decoration: none;
color: white;
}
`}
</style>
</footer>
);
};
export default Footer;
================================================
FILE: nextjs-app/src/components/List.tsx
================================================
/**
* List Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { UserCard } from './Card';
interface User {
name: string;
_id: string;
email: string;
}
interface ListProps {
data: [User];
img: string;
}
const List: React.SFC<ListProps> = props => {
return (
<>
{props.data.map((user: User) => (
<UserCard key={user._id} user={user} img={props.img} />
))}
<style jsx>
{`
.List:hover {
transform: scale(1.1);
}
h2 {
font-size: 1rem;
text-align: right;
}
img {
margin-right: 2rem;
}
`}
</style>
</>
);
};
export default List;
================================================
FILE: nextjs-app/src/components/Login.tsx
================================================
/**
* Login Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Router from 'next/router';
import { toast } from 'react-toastify';
import { ApolloConsumer } from 'react-apollo';
import LOGIN_USER from '../graphql/query/login';
import { validateEmail } from '../utils/validation';
import { setToken } from '../configureClient';
import Cookies from 'js-cookie';
interface LoginState {
[key: string]: any;
email: string;
password: string;
}
class Login extends React.PureComponent<any, LoginState> {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
};
}
handleChange = (event: any) => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = async (event: any, client: any) => {
try {
event.preventDefault();
const { state } = this;
if (validateEmail(state.email)) {
const { data } = await client.query({
query: LOGIN_USER,
variables: { ...state },
});
const { token, userId } = data.login;
setToken(token);
Cookies.set('userId', userId, { expires: 7 });
Router.replace('/welcome');
} else {
toast.error('Invalid Email');
}
} catch (error) {
toast.error('Not Authenticated');
}
};
render() {
const { state } = this;
return (
<ApolloConsumer>
{client => (
<form
onSubmit={e => this.handleSubmit(e, client)}
className="login-form">
<input
type="text"
placeholder="Email"
name="email"
value={state.email}
onChange={this.handleChange}
className="login-input-box"
required
/>
<input
type="password"
placeholder="Password"
name="password"
value={state.password}
onChange={this.handleChange}
className="login-input-box"
required
/>
<input type="submit" value="Submit" className="login-button" />
<p onClick={() => Router.push('/signup')}>
New user ? <b>Sign Up</b>
</p>
<style jsx>
{`
form {
display: flex;
flex-flow: column wrap;
justify-content: center;
align-items: center;
border-left: 1px solid white;
padding: 2rem;
}
p {
color: white;
cursor: pointer;
}
.login-input-box {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
border: 0;
width: 15rem;
padding: 0.5rem;
height: 2rem;
margin-bottom: 2rem;
font-family: Candara;
}
.login-button {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
width: 15rem;
font-size: 0.9rem;
padding: 0.5rem;
height: 3rem;
margin: 2rem;
font-family: Candara;
transition: transform 0.2s;
cursor: pointer;
}
.login-button:hover {
transform: scale(1.1);
background: #355c7d;
background: -webkit-linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
background: linear-gradient(
to right,
#c06c84,
#6c5b7b,
#355c7d
);
color: white;
}
@media only screen and (max-width: 740px) {
form {
border-left: none;
border-top: 1px solid white;
}
}
input:focus {
outline: none;
}
`}
</style>
</form>
)}
</ApolloConsumer>
);
}
}
export default Login;
================================================
FILE: nextjs-app/src/config/index.ts
================================================
/**
* Configuration
* @author Anurag Garg <garganurag893@gmail.com>
*/
export const SERVER = 'http://localhost:4020/graphql';
export const WEB_SOCKET_LINK = 'ws://localhost:4020/graphql';
================================================
FILE: nextjs-app/src/configureClient.ts
================================================
/**
* Apollo Client Configuration
* @author Anurag Garg <garganurag893@gmail.com>
*/
import { ApolloClient } from 'apollo-client';
import { split, ApolloLink, concat } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { getMainDefinition } from 'apollo-utilities';
import withApollo from 'next-with-apollo';
import { HttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
import { WebSocketLink } from 'apollo-link-ws';
import Cookies from 'js-cookie';
import { SERVER, WEB_SOCKET_LINK } from './config';
interface Definintion {
kind: string;
operation?: string;
}
let authToken = null;
const httpLink = new HttpLink({
fetch,
uri: SERVER,
});
const authMiddleware = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
authorization: authToken || null,
},
});
// Add onto payload for WebSocket authentication
(operation as any & { authToken: string | undefined }).authToken = authToken;
return forward(operation);
});
const webSocketLink: any = process.browser
? new WebSocketLink({
uri: WEB_SOCKET_LINK,
options: {
reconnect: true,
},
})
: null;
/**
* Set Token
* @param token
*/
export const setToken = async (token: string) => {
try {
authToken = token ? `Bearer ${token}` : null;
Cookies.set('token', authToken, { expires: 7 });
} catch (error) {
console.log(error);
}
};
/**
* Set Token In Request
* @param token
*/
export const setTokenInRequest = async (token: string) => {
try {
authToken = token ? token : null;
return authToken;
} catch (error) {
console.log(error);
}
};
/**
* Destroy Token
* For logout purpose
*/
export const destroyToken = async () => {
try {
Cookies.remove('token');
authToken = null;
} catch (error) {
console.log(error);
}
};
const link = process.browser
? split(
({ query }) => {
const { kind, operation }: Definintion = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
webSocketLink,
httpLink
)
: httpLink;
export default withApollo(
({ initialState }) =>
new ApolloClient({
link: concat(authMiddleware, link),
cache: new InMemoryCache().restore(initialState || {}),
})
);
================================================
FILE: nextjs-app/src/graphql/mutation/createUser.ts
================================================
/**
* Create User Mutation
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const CREATE_USER = gql`
mutation createUser($userInput: UserInput) {
createUser(userInput: $userInput) {
token
userId
}
}
`;
export default CREATE_USER;
================================================
FILE: nextjs-app/src/graphql/mutation/updateUser.ts
================================================
/**
* Update user mutation
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const UPDATE_USER = gql`
mutation updateUser($userId: ID!, $updateUser: UpdateUser) {
updateUser(userId: $userId, updateUser: $updateUser) {
_id
name
email
}
}
`;
export default UPDATE_USER;
================================================
FILE: nextjs-app/src/graphql/query/login.ts
================================================
/**
* Login user query
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const LOGIN_USER = gql`
query login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
userId
}
}
`;
export default LOGIN_USER;
================================================
FILE: nextjs-app/src/graphql/query/user.ts
================================================
/**
* Get all users query
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const GET_USERS = gql`
{
users {
name
_id
email
}
}
`;
export default GET_USERS;
================================================
FILE: nextjs-app/src/graphql/subscription/users.ts
================================================
/**
* New user added subscription
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const USER_ADDED = gql`
subscription {
userAdded {
name
_id
email
}
}
`;
export default USER_ADDED;
================================================
FILE: nextjs-app/src/utils/auth.tsx
================================================
/**
* Auth Middlerware Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import * as React from 'react';
import Router from 'next/router';
import nextCookie from 'next-cookies';
import { setTokenInRequest } from '../configureClient';
const getDisplayName = Component =>
Component.displayName || Component.name || 'Component';
export const auth = ctx => {
const { token, userId } = nextCookie(ctx);
if (ctx.req && !token) {
ctx.res.writeHead(302, { Location: '/' });
ctx.res.end();
return;
}
if (!token) {
Router.push('/');
}
return { token, userId };
};
export const withAuthSync = WrappedComponent =>
class extends React.Component {
static displayName = `withAuthSync(${getDisplayName(WrappedComponent)})`;
static async getInitialProps(ctx) {
const { token, userId } = auth(ctx);
await setTokenInRequest(token);
const componentProps =
WrappedComponent.getInitialProps &&
(await WrappedComponent.getInitialProps(ctx));
return { ...componentProps, token, userId };
}
render() {
return <WrappedComponent {...this.props} />;
}
};
================================================
FILE: nextjs-app/src/utils/validation.ts
================================================
/**
* Email Validation
* @author Anurag Garg <garganurag893@gmail.com>
*/
const validateEmail = (email: string): boolean => {
if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email)) {
return true;
}
return false;
};
export { validateEmail };
================================================
FILE: nextjs-app/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es6",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": [
"node_modules"
],
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx"
]
}
================================================
FILE: react-app/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'react-app',
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'react'],
rules: {
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/no-explicit-any': 0,
},
};
================================================
FILE: react-app/.gitignore
================================================
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
================================================
FILE: react-app/.prettierrc.js
================================================
module.exports = {
useTabs: false,
printWidth: 80,
singleQuote: true,
trailingComma: 'es5',
jsxBracketSameLine: true,
noSemi: false,
};
================================================
FILE: react-app/package.json
================================================
{
"name": "react-app",
"version": "0.1.0",
"private": true,
"author": "Anurag Garg",
"license": "MIT",
"dependencies": {
"@apollo/react-components": "^3.1.3",
"@apollo/react-hooks": "^3.1.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^24.0.0",
"@types/node": "^12.0.0",
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"apollo-cache-inmemory": "^1.6.5",
"apollo-client": "^2.6.8",
"apollo-link-batch-http": "^1.2.13",
"apollo-link-http": "^1.5.16",
"apollo-link-ws": "^1.0.19",
"graphql": "^14.5.8",
"graphql-tag": "^2.10.1",
"isomorphic-unfetch": "^3.0.0",
"js-cookie": "^2.2.1",
"node-sass": "^4.13.1",
"react": "^16.12.0",
"react-apollo": "^3.1.3",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.0",
"react-toastify": "^5.5.0",
"subscriptions-transport-ws": "^0.9.16",
"typescript": "~3.7.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"lint": "eslint './**/*.{ts,js,tsx}'"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"./**/*.{js,jsx,ts,tsx,css}": [
"prettier --write",
"yarn lint"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/js-cookie": "^2.2.4",
"@types/react-router-dom": "^5.1.3",
"@typescript-eslint/eslint-plugin": "^2.16.0",
"@typescript-eslint/parser": "^2.16.0",
"babel-eslint": "^10.0.3",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"eslint-config-react-app": "^5.1.0",
"eslint-plugin-flowtype": "^4",
"eslint-plugin-import": "^2.20.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.18.0",
"eslint-plugin-react-hooks": "^2.3.0",
"husky": "^4.2.0",
"lint-staged": "^10.0.2",
"prettier": "^1.19.1"
}
}
================================================
FILE: react-app/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
================================================
FILE: react-app/public/manifest.json
================================================
{
"short_name": "React GraphQL Boilerplate App",
"name": "React GraphQL Boilerplate App",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
================================================
FILE: react-app/src/App.test.tsx
================================================
/**
* Test File
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders welcome heading', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/Welcome/i);
expect(linkElement).toBeInTheDocument();
});
================================================
FILE: react-app/src/App.tsx
================================================
/**
* App Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { ApolloProvider } from '@apollo/react-hooks';
import apolloClient from './configureClient';
import PrivateRoute from './utils/auth';
import Login from './screens/Login';
import SignUp from './screens/SignUp';
import Welcome from './screens/Welcome';
import Users from './screens/Users';
import Update from './screens/Update';
import NoMatch from './screens/NoMatch';
import Subscription from './screens/Subscription';
const App = () => {
return (
<ApolloProvider client={apolloClient}>
<Router>
<Switch>
<Route exact path="/" component={Login} />
<Route path="/signup" component={SignUp} />
<PrivateRoute path="/welcome" component={Welcome} />
<PrivateRoute path="/users" component={Users} />
<PrivateRoute path="/update" component={Update} />
<PrivateRoute path="/subscription" component={Subscription} />
<Route path="*" component={NoMatch} />
</Switch>
</Router>
<ToastContainer
position="top-right"
autoClose={2000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
draggable
pauseOnHover
/>
</ApolloProvider>
);
};
export default App;
================================================
FILE: react-app/src/components/Card/index.tsx
================================================
/**
* Card Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import './styles.scss';
import { Link } from 'react-router-dom';
interface User {
name: string;
_id: string;
email: string;
}
interface CardProps {
title: string;
href: string;
}
interface UserCardProps {
user: User;
img: string;
}
const Card: React.SFC<CardProps> = props => {
return (
<Link to={props.href} className="link">
<div className="card">
<h2>{props.title}</h2>
</div>
</Link>
);
};
export const UserCard: React.SFC<UserCardProps> = props => {
return (
<div key={props.user._id} className="listCard">
<img src={props.img} alt="user" height="90" />
<div className="userCardDetails">
<h2>{props.user.name}</h2>
<h2>{props.user.email}</h2>
</div>
</div>
);
};
export default Card;
================================================
FILE: react-app/src/components/Card/styles.scss
================================================
.card {
display: flex;
height: 5rem;
width: 20rem;
justify-content: center;
padding: 2rem;
align-self: stretch;
align-items: center;
background-color: white;
margin: 2rem;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
transition: transform 0.2s;
cursor: pointer;
&:hover {
transform: scale(1.1);
}
h2 {
font-size: 2rem;
}
}
.listCard {
display: flex;
flex-flow: wrap row;
justify-content: space-between;
padding: 2rem;
-webkit-align-self: stretch;
align-self: stretch;
align-items: center;
background-color: white;
margin: 2rem;
width: 20rem;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
transition: transform 0.2s;
.userCardDetails {
display: flex;
flex-flow: wrap column;
justify-content: space-between;
align-items: flex-end;
height: 3rem;
}
h2 {
font-size: 1rem;
text-align: right;
}
img {
margin-right: 2rem;
}
}
.link {
text-decoration: none;
color: black;
}
================================================
FILE: react-app/src/components/Footer/index.tsx
================================================
/**
* Footer Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import './styles.scss';
const Footer: React.SFC<any> = props => {
return (
<footer className="footer">
<div className="footer-text-container">
<p>
© Copyright 2020{' '}
<a href="https://twitter.com/AnuragG94634191">Anurag Garg</a>
</p>
</div>
</footer>
);
};
export default Footer;
================================================
FILE: react-app/src/components/Footer/styles.scss
================================================
.footer {
color: rgba(255, 255, 255, 0.5);
margin-top: auto !important;
.footer-text-container {
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
text-shadow: 0 0.05rem 0.1rem rgba(0, 0, 0, 0.5);
text-align: center !important;
color: rgba(255, 255, 255, 0.5);
}
a {
text-decoration: none;
color: white;
}
}
================================================
FILE: react-app/src/components/List/index.tsx
================================================
/**
* List Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { UserCard } from '../Card';
import './styles.scss';
interface User {
name: string;
_id: string;
email: string;
}
interface ListProps {
data: [User];
img: string;
}
const List: React.SFC<ListProps> = props => {
return (
<>
{props.data.map((user: User) => (
<UserCard key={user._id} user={user} img={props.img} />
))}
</>
);
};
export default List;
================================================
FILE: react-app/src/components/List/styles.scss
================================================
.List {
&:hove {
transform: scale(1.1);
}
h2 {
font-size: 1rem;
text-align: right;
}
img {
margin-right: 2rem;
}
}
================================================
FILE: react-app/src/components/LoginForm/index.tsx
================================================
/**
* Login Form Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { toast } from 'react-toastify';
import { ApolloConsumer } from 'react-apollo';
import LOGIN_USER from '../../graphql/query/login';
import { validateEmail } from '../../utils/validation';
import { setToken } from '../../configureClient';
import { Link } from 'react-router-dom';
import Cookies from 'js-cookie';
import './styles.scss';
interface LoginFormState {
[key: string]: any;
email: string;
password: string;
}
class LoginForm extends React.PureComponent<any, LoginFormState> {
constructor(props: any) {
super(props);
this.state = {
email: '',
password: '',
};
}
handleChange = (event: any) => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = async (event: any, client: any) => {
try {
event.preventDefault();
const { state, props } = this;
if (validateEmail(state.email)) {
const { data } = await client.query({
query: LOGIN_USER,
variables: { ...state },
});
const { token, userId } = data.login;
setToken(token);
Cookies.set('userId', userId, { expires: 7 });
props.history.replace('/welcome');
} else {
toast.error('Invalid Email');
}
} catch (error) {
toast.error('Not Authenticated');
}
};
render() {
const { state } = this;
return (
<ApolloConsumer>
{client => (
<form
onSubmit={e => this.handleSubmit(e, client)}
className="login-form">
<input
type="text"
placeholder="Email"
name="email"
value={state.email}
onChange={this.handleChange}
className="login-input-box"
required
/>
<input
type="password"
placeholder="Password"
name="password"
value={state.password}
onChange={this.handleChange}
className="login-input-box"
required
/>
<input type="submit" value="Submit" className="login-button" />
<Link to="/signup" className="signup-link">
<p>
New user ? <b>Sign Up</b>
</p>
</Link>
</form>
)}
</ApolloConsumer>
);
}
}
export default LoginForm;
================================================
FILE: react-app/src/components/LoginForm/styles.scss
================================================
.login-form {
display: flex;
flex-flow: column wrap;
justify-content: center;
align-items: center;
border-left: 1px solid white;
padding: 2rem;
p {
color: white;
cursor: pointer;
}
}
.login-input-box {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
border: 0;
width: 15rem;
padding: 0.5rem;
height: 2rem;
margin-bottom: 2rem;
font-family: Candara;
}
.login-button {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
width: 15rem;
font-size: 0.9rem;
padding: 0.5rem;
height: 3rem;
margin: 2rem;
font-family: Candara;
transition: transform 0.2s;
cursor: pointer;
}
.login-button:hover {
transform: scale(1.1);
background: #355c7d;
background: -webkit-linear-gradient(to right,
#c06c84,
#6c5b7b,
#355c7d);
background: linear-gradient(to right,
#c06c84,
#6c5b7b,
#355c7d);
color: white;
}
.signup-link {
text-decoration: none;
}
@media only screen and (max-width: 740px) {
.login-form {
border-left: none;
border-top: 1px solid white;
}
}
input:focus {
outline: none;
}
================================================
FILE: react-app/src/config/index.ts
================================================
/**
* Configuration
* @author Anurag Garg <garganurag893@gmail.com>
*/
export const SERVER = 'http://localhost:4020/graphql';
export const WEB_SOCKET_LINK = 'ws://localhost:4020/graphql';
================================================
FILE: react-app/src/configureClient.ts
================================================
/**
* Apollo Client Configuration
* @author Anurag Garg <garganurag893@gmail.com>
*/
import { ApolloClient } from 'apollo-client';
import { split, ApolloLink, concat } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { getMainDefinition } from 'apollo-utilities';
import { HttpLink } from 'apollo-link-http';
import fetch from 'isomorphic-unfetch';
import { WebSocketLink } from 'apollo-link-ws';
import Cookies from 'js-cookie';
import { SERVER, WEB_SOCKET_LINK } from './config';
interface Definintion {
kind: string;
operation?: string;
}
let authToken = '';
const httpLink = new HttpLink({
fetch,
uri: SERVER,
});
const authMiddleware = new ApolloLink((operation, forward) => {
operation.setContext({
headers: {
authorization: authToken || null,
},
});
// Add onto payload for WebSocket authentication
(operation as any & { authToken: string | undefined }).authToken = authToken;
return forward(operation);
});
const webSocketLink: WebSocketLink = new WebSocketLink({
uri: WEB_SOCKET_LINK,
options: {
reconnect: true,
},
});
/**
* Set Token
* @param token
*/
export const setToken = async (token: string | undefined) => {
try {
authToken = token ? `Bearer ${token}` : '';
Cookies.set('token', authToken, { expires: 7 });
} catch (error) {
console.log(error);
}
};
/**
* Get Token & Set Token In Request
*/
export const getToken = async () => {
try {
const token = Cookies.get('token');
authToken = token ? token : '';
return authToken;
} catch (error) {
console.log(error);
}
};
/**
* Destroy Token
* For logout purpose
*/
export const destroyToken = async () => {
try {
Cookies.remove('token');
authToken = '';
} catch (error) {
console.log(error);
}
};
const link = split(
({ query }) => {
const { kind, operation }: Definintion = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
webSocketLink,
httpLink
);
const client = new ApolloClient({
link: concat(authMiddleware, link),
cache: new InMemoryCache().restore({}),
connectToDevTools: true,
});
export default client;
================================================
FILE: react-app/src/graphql/mutation/createUser.ts
================================================
/**
* Create User Mutation
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const CREATE_USER = gql`
mutation createUser($userInput: UserInput) {
createUser(userInput: $userInput) {
token
userId
}
}
`;
export default CREATE_USER;
================================================
FILE: react-app/src/graphql/mutation/updateUser.ts
================================================
/**
* Update user mutation
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const UPDATE_USER = gql`
mutation updateUser($userId: ID!, $updateUser: UpdateUser) {
updateUser(userId: $userId, updateUser: $updateUser) {
_id
name
email
}
}
`;
export default UPDATE_USER;
================================================
FILE: react-app/src/graphql/query/login.ts
================================================
/**
* Login user query
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const LOGIN_USER = gql`
query login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
userId
}
}
`;
export default LOGIN_USER;
================================================
FILE: react-app/src/graphql/query/user.ts
================================================
/**
* Get all users query
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const GET_USERS = gql`
{
users {
name
_id
email
}
}
`;
export default GET_USERS;
================================================
FILE: react-app/src/graphql/subscription/users.ts
================================================
/**
* New user added subscription
* @author Anurag Garg <garganurag893@gmail.com>
*/
import gql from 'graphql-tag';
const USER_ADDED = gql`
subscription {
userAdded {
name
_id
email
}
}
`;
export default USER_ADDED;
================================================
FILE: react-app/src/index.css
================================================
h1,
h2 {
margin: 0;
font-family: Candara;
}
body {
margin: 0;
padding: 0;
height: 100%;
min-height: 100vh;
font-family: Candara;
background: #355c7d;
background: -webkit-linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
}
================================================
FILE: react-app/src/index.tsx
================================================
/**
* Primary file for react
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
================================================
FILE: react-app/src/react-app-env.d.ts
================================================
/// <reference types="react-scripts" />
================================================
FILE: react-app/src/screens/Login/index.tsx
================================================
/**
* Main Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import LoginForm from '../../components/LoginForm';
import Footer from '../../components/Footer';
import './styles.scss';
const Login: React.SFC = (props: any) => {
return (
<div className="loginPage-mainContainer">
<div className="loginPage-container">
<h1 className="heading">Welcome</h1>
<LoginForm {...props} />
</div>
<Footer />
</div>
);
};
export default Login;
================================================
FILE: react-app/src/screens/Login/styles.scss
================================================
.loginPage-mainContainer {
height: 100%;
min-height: 100vh;
width: 100%;
.loginPage-container {
display: flex;
justify-content: center;
align-items: center;
flex-flow: row wrap;
padding: 8rem 0rem !important;
}
.heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 2rem;
}
@media only screen and (max-width: 740px) {
.loginPage-container {
padding: 3rem 0rem !important;
}
}
}
================================================
FILE: react-app/src/screens/NoMatch/index.tsx
================================================
/**
* No Match Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Footer from '../../components/Footer';
import './styles.scss';
const NoMatch: React.SFC = () => {
return (
<div className="nomatch-container">
<h1 className="nomatch-heading">No Match Found | 404</h1>
<Footer />
</div>
);
};
export default NoMatch;
================================================
FILE: react-app/src/screens/NoMatch/styles.scss
================================================
.nomatch-container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.nomatch-heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 5rem 0rem;
}
================================================
FILE: react-app/src/screens/SignUp/index.tsx
================================================
/**
* SignUp Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { toast } from 'react-toastify';
import Footer from '../../components/Footer';
import Cookies from 'js-cookie';
import { Mutation } from '@apollo/react-components';
import CREATE_USER from '../../graphql/mutation/createUser';
import { setToken } from '../../configureClient';
import { validateEmail } from '../../utils/validation';
import './styles.scss';
interface SignUpState {
[key: string]: any;
name: string;
email: string;
password: string;
}
class SignUp extends React.PureComponent<any, SignUpState> {
constructor(props: any) {
super(props);
this.state = {
name: '',
email: '',
password: '',
};
}
handleChange = (event: any) => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = async (createUser: any, event: any) => {
try {
event.preventDefault();
const { state, props } = this;
if (validateEmail(state.email)) {
const data = await createUser({
variables: {
userInput: { ...state },
},
});
const { token, userId } = data.createUser;
setToken(token);
Cookies.set('userId', userId, { expires: 7 });
props.history.replace('/welcome');
} else {
toast.error('Invalid Email');
}
} catch (error) {
toast.error('Check your connection');
}
};
render() {
const { state } = this;
return (
<div className="signup-container">
<h1 className="signup-heading">Sign Up</h1>
<Mutation mutation={CREATE_USER}>
{(createUser: any) => (
<form
onSubmit={event => this.handleSubmit(createUser, event)}
className="signup-form">
<input
type="text"
placeholder="Name"
name="name"
value={state.name}
onChange={this.handleChange}
className="signup-input-box"
required
/>
<input
type="text"
placeholder="Email"
name="email"
value={state.email}
onChange={this.handleChange}
className="signup-input-box"
required
/>
<input
type="password"
placeholder="Password"
name="password"
value={state.password}
onChange={this.handleChange}
className="signup-input-box"
required
/>
<input type="submit" value="Submit" className="signup-button" />
</form>
)}
</Mutation>
<Footer />
</div>
);
}
}
export default SignUp;
================================================
FILE: react-app/src/screens/SignUp/styles.scss
================================================
.signup-container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.signup-heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 5rem 0rem;
}
.signup-form {
display: flex;
flex-flow: column wrap;
justify-content: center;
align-items: center;
}
.signup-input-box {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
border: 0;
width: 15rem;
padding: 0.5rem;
height: 2rem;
margin-bottom: 2rem;
font-family: Candara;
}
.signup-button {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
width: 15rem;
font-size: 0.9rem;
padding: 0.5rem;
height: 3rem;
margin: 2rem;
font-family: Candara;
transition: transform 0.2s;
cursor: pointer;
&:hover {
transform: scale(1.1);
background: #355c7d;
background: -webkit-linear-gradient(to right,
#c06c84,
#6c5b7b,
#355c7d);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
color: white;
}
}
input:focus {
outline: none;
}
================================================
FILE: react-app/src/screens/Subscription/index.tsx
================================================
/**
* Subscription Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { useSubscription } from '@apollo/react-hooks';
import { UserCard } from '../../components/Card';
import USER_ADDED from '../../graphql/subscription/users';
import './styles.scss';
const Subscription = () => {
const { data, loading, error } = useSubscription(USER_ADDED);
let message = 'New User';
if (loading) message = 'Listening...';
if (error) message = `Error! ${error.message}`;
if (data && data.userAdded.length <= 0) message = 'No New User Added';
return (
<div className="subscription-container">
<h1 className="subscription-heading">{message}</h1>
{data && data.userAdded && (
<div className="subscription-listContainer">
<UserCard img="./user.png" user={data.userAdded} />
</div>
)}
</div>
);
};
export default Subscription;
================================================
FILE: react-app/src/screens/Subscription/styles.scss
================================================
.subscription-container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.subscription-listContainer {
display: flex;
flex-flow: wrap row;
justify-content: space-evenly;
align-items: center;
}
.subscription-heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 0rem 0 5rem;
}
================================================
FILE: react-app/src/screens/Update/index.tsx
================================================
/**
* Update Profile Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import { toast } from 'react-toastify';
import Footer from '../../components/Footer';
import { Mutation } from '@apollo/react-components';
import { validateEmail } from '../../utils/validation';
import UPDATE_USER from '../../graphql/mutation/updateUser';
import './styles.scss';
interface UpdateState {
[key: string]: any;
name: string;
email: string;
password: string;
}
class Update extends React.PureComponent<any, UpdateState> {
constructor(props: any) {
super(props);
this.state = {
name: '',
email: '',
password: '',
};
}
handleChange = (event: any) => {
this.setState({ [event.target.name]: event.target.value });
};
handleSubmit = async (updateUser: any, event: any) => {
try {
event.preventDefault();
const { state, props } = this;
if (validateEmail(state.email)) {
await updateUser({
variables: {
userId: props.userId,
updateUser: { ...state },
},
});
toast.success('Profile Updated');
props.history.push('/welcome');
} else {
toast.error('Invalid Email');
}
} catch (error) {
toast.error('Check your connection');
}
};
render() {
const { state } = this;
return (
<div className="update-container">
<h1 className="update-heading">Update Profile</h1>
<Mutation mutation={UPDATE_USER}>
{(updateUser: any) => (
<form
onSubmit={event => this.handleSubmit(updateUser, event)}
className="update-form">
<input
type="text"
placeholder="Name"
name="name"
value={state.name}
onChange={this.handleChange}
className="update-input-box"
required
/>
<input
type="text"
placeholder="Email"
name="email"
value={state.email}
onChange={this.handleChange}
className="update-input-box"
required
/>
<input
type="password"
placeholder="Password"
name="password"
value={state.password}
onChange={this.handleChange}
className="update-input-box"
required
/>
<input type="submit" value="Submit" className="update-button" />
</form>
)}
</Mutation>
<Footer />
</div>
);
}
}
export default Update;
================================================
FILE: react-app/src/screens/Update/styles.scss
================================================
.update-container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.update-heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 5rem 0rem;
}
.update-form {
display: flex;
flex-flow: column wrap;
justify-content: center;
align-items: center;
}
.update-input-box {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
border: 0;
width: 15rem;
padding: 0.5rem;
height: 2rem;
margin-bottom: 2rem;
font-family: Candara;
}
.update-button {
background-color: white;
border-radius: 14px 0px 14px 1px;
-moz-border-radius: 14px 0px 14px 1px;
-webkit-border-radius: 14px 0px 14px 1px;
border: 0px solid #000000;
box-shadow: 0 20px 30px -16px rgba(9, 9, 16, 0.2);
width: 15rem;
font-size: 0.9rem;
padding: 0.5rem;
height: 3rem;
margin: 2rem;
font-family: Candara;
transition: transform 0.2s;
cursor: pointer;
}
.update-button:hover {
transform: scale(1.1);
background: #355c7d;
background: -webkit-linear-gradient(to right,
#c06c84,
#6c5b7b,
#355c7d);
background: linear-gradient(to right, #c06c84, #6c5b7b, #355c7d);
color: white;
}
input:focus {
outline: none;
}
================================================
FILE: react-app/src/screens/Users/index.tsx
================================================
/**
* User List Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import List from '../../components/List';
import { useQuery } from '@apollo/react-hooks';
import GET_USERS from '../../graphql/query/user';
import './styles.scss';
const Users = () => {
const { loading, error, data } = useQuery(GET_USERS);
let message = 'Users';
if (loading) message = 'Loading...';
if (error) message = `Error! ${error}`;
if (data && data.users.length <= 0) message = 'No Users';
return (
<div className="users-container">
<h1 className="users-heading">{message}</h1>
{data && data.users.length > 0 && (
<div className="users-listContainer">
<List img="./user.png" data={data.users} />
</div>
)}
</div>
);
};
export default Users;
================================================
FILE: react-app/src/screens/Users/styles.scss
================================================
.users-container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.users-listContainer {
display: flex;
flex-flow: wrap row;
justify-content: space-evenly;
align-items: center;
}
.users-heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 0rem 0 5rem;
}
================================================
FILE: react-app/src/screens/Welcome/index.tsx
================================================
/**
* Welcome Page
* @author Anurag Garg <garganurag893@gmail.com>
*/
import React from 'react';
import Card from '../../components/Card';
import Footer from '../../components/Footer';
import './styles.scss';
const Welcome: React.SFC = () => {
return (
<div className="welcome-container">
<h1 className="welcome-heading">Welcome</h1>
<div className="welcome-cardContainer">
<Card title="Query" href="/users" />
<Card title="Mutation" href="/update" />
<Card title="Subscription" href="/subscription" />
</div>
<Footer />
</div>
);
};
export default Welcome;
================================================
FILE: react-app/src/screens/Welcome/styles.scss
================================================
.welcome-container {
height: 100%;
min-height: 100vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column wrap;
}
.welcome-cardContainer {
display: flex;
flex-flow: wrap row;
justify-content: center;
align-items: center;
}
.welcome-heading {
color: white;
text-align: center;
font-size: 5rem;
padding: 6rem 0rem;
}
================================================
FILE: react-app/src/setupTests.ts
================================================
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
================================================
FILE: react-app/src/utils/auth.tsx
================================================
/**
* Auth Middlerware Component
* @author Anurag Garg <garganurag893@gmail.com>
*/
import * as React from 'react';
import { setToken } from '../configureClient';
import Cookies from 'js-cookie';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({ children, ...rest }: any) => {
const token = Cookies.get('token');
setToken(token);
if (!token) {
return <Redirect to="/" />;
}
return (
<Route
{...rest}
render={({ location }: any) =>
token ? (
children
) : (
<Redirect
to={{
pathname: '/',
state: { from: location },
}}
/>
)
}
/>
);
};
export default PrivateRoute;
================================================
FILE: react-app/src/utils/validation.ts
================================================
/* eslint-disable no-useless-escape */
/**
* Email Validation
* @author Anurag Garg <garganurag893@gmail.com>
*/
const validateEmail = (email: string): boolean => {
if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email)) {
return true;
}
return false;
};
export { validateEmail };
================================================
FILE: react-app/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": [
"src"
]
}
gitextract_51xeknsa/
├── .github/
│ ├── FUNDING.yml
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── Express_GraphQL_Apollo_Mongodb_Server/
│ ├── .gitignore
│ ├── config/
│ │ ├── express.ts
│ │ └── index.ts
│ ├── index.ts
│ ├── package.json
│ ├── server/
│ │ ├── graphql/
│ │ │ ├── resolvers/
│ │ │ │ ├── index.ts
│ │ │ │ ├── merge.ts
│ │ │ │ └── user.ts
│ │ │ └── schema/
│ │ │ └── index.ts
│ │ ├── helpers/
│ │ │ └── date.ts
│ │ ├── middleware/
│ │ │ └── auth.ts
│ │ └── models/
│ │ └── user.ts
│ ├── tsconfig.json
│ └── tslint.json
├── LICENSE
├── README.md
├── nextjs-app/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc.js
│ ├── next-env.d.ts
│ ├── package.json
│ ├── pages/
│ │ ├── _app.tsx
│ │ ├── index.tsx
│ │ ├── signup.tsx
│ │ ├── subscription.tsx
│ │ ├── update.tsx
│ │ ├── users.tsx
│ │ └── welcome.tsx
│ ├── src/
│ │ ├── components/
│ │ │ ├── Card.tsx
│ │ │ ├── Footer.tsx
│ │ │ ├── List.tsx
│ │ │ └── Login.tsx
│ │ ├── config/
│ │ │ └── index.ts
│ │ ├── configureClient.ts
│ │ ├── graphql/
│ │ │ ├── mutation/
│ │ │ │ ├── createUser.ts
│ │ │ │ └── updateUser.ts
│ │ │ ├── query/
│ │ │ │ ├── login.ts
│ │ │ │ └── user.ts
│ │ │ └── subscription/
│ │ │ └── users.ts
│ │ └── utils/
│ │ ├── auth.tsx
│ │ └── validation.ts
│ └── tsconfig.json
└── react-app/
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── package.json
├── public/
│ ├── index.html
│ └── manifest.json
├── src/
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components/
│ │ ├── Card/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Footer/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── List/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ └── LoginForm/
│ │ ├── index.tsx
│ │ └── styles.scss
│ ├── config/
│ │ └── index.ts
│ ├── configureClient.ts
│ ├── graphql/
│ │ ├── mutation/
│ │ │ ├── createUser.ts
│ │ │ └── updateUser.ts
│ │ ├── query/
│ │ │ ├── login.ts
│ │ │ └── user.ts
│ │ └── subscription/
│ │ └── users.ts
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── screens/
│ │ ├── Login/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── NoMatch/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── SignUp/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Subscription/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Update/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── Users/
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ └── Welcome/
│ │ ├── index.tsx
│ │ └── styles.scss
│ ├── setupTests.ts
│ └── utils/
│ ├── auth.tsx
│ └── validation.ts
└── tsconfig.json
SYMBOL INDEX (61 symbols across 30 files)
FILE: Express_GraphQL_Apollo_Mongodb_Server/config/express.ts
class Express (line 14) | class Express {
method origin (line 27) | origin(origin, callback) {
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/user.ts
constant USER_ADDED (line 14) | const USER_ADDED = 'USER_ADDED';
FILE: Express_GraphQL_Apollo_Mongodb_Server/server/models/user.ts
method get (line 39) | get(id: string): mongoose.Document {
FILE: nextjs-app/pages/_app.tsx
class MyApp (line 13) | class MyApp extends App<any> {
method render (line 14) | render() {
FILE: nextjs-app/pages/signup.tsx
type SignUpState (line 16) | interface SignUpState {
class SignUp (line 23) | class SignUp extends React.PureComponent<any, SignUpState> {
method constructor (line 24) | constructor(props) {
method render (line 58) | render() {
FILE: nextjs-app/pages/update.tsx
type UpdateState (line 15) | interface UpdateState {
class Update (line 22) | class Update extends React.PureComponent<any, UpdateState> {
method constructor (line 23) | constructor(props) {
method render (line 56) | render() {
FILE: nextjs-app/pages/users.tsx
type User (line 11) | interface User {
type Data (line 17) | interface Data {
type UserProps (line 21) | interface UserProps {
FILE: nextjs-app/src/components/Card.tsx
type User (line 9) | interface User {
type CardProps (line 15) | interface CardProps {
type UserCardProps (line 20) | interface UserCardProps {
FILE: nextjs-app/src/components/List.tsx
type User (line 9) | interface User {
type ListProps (line 15) | interface ListProps {
FILE: nextjs-app/src/components/Login.tsx
type LoginState (line 15) | interface LoginState {
class Login (line 21) | class Login extends React.PureComponent<any, LoginState> {
method constructor (line 22) | constructor(props) {
method render (line 55) | render() {
FILE: nextjs-app/src/config/index.ts
constant SERVER (line 6) | const SERVER = 'http://localhost:4020/graphql';
constant WEB_SOCKET_LINK (line 7) | const WEB_SOCKET_LINK = 'ws://localhost:4020/graphql';
FILE: nextjs-app/src/configureClient.ts
type Definintion (line 17) | interface Definintion {
FILE: nextjs-app/src/graphql/mutation/createUser.ts
constant CREATE_USER (line 8) | const CREATE_USER = gql`
FILE: nextjs-app/src/graphql/mutation/updateUser.ts
constant UPDATE_USER (line 8) | const UPDATE_USER = gql`
FILE: nextjs-app/src/graphql/query/login.ts
constant LOGIN_USER (line 8) | const LOGIN_USER = gql`
FILE: nextjs-app/src/graphql/query/user.ts
constant GET_USERS (line 8) | const GET_USERS = gql`
FILE: nextjs-app/src/graphql/subscription/users.ts
constant USER_ADDED (line 8) | const USER_ADDED = gql`
FILE: nextjs-app/src/utils/auth.tsx
method getInitialProps (line 34) | static async getInitialProps(ctx) {
method render (line 44) | render() {
FILE: react-app/src/components/Card/index.tsx
type User (line 10) | interface User {
type CardProps (line 16) | interface CardProps {
type UserCardProps (line 21) | interface UserCardProps {
FILE: react-app/src/components/List/index.tsx
type User (line 10) | interface User {
type ListProps (line 16) | interface ListProps {
FILE: react-app/src/components/LoginForm/index.tsx
type LoginFormState (line 16) | interface LoginFormState {
class LoginForm (line 22) | class LoginForm extends React.PureComponent<any, LoginFormState> {
method constructor (line 23) | constructor(props: any) {
method render (line 56) | render() {
FILE: react-app/src/config/index.ts
constant SERVER (line 6) | const SERVER = 'http://localhost:4020/graphql';
constant WEB_SOCKET_LINK (line 7) | const WEB_SOCKET_LINK = 'ws://localhost:4020/graphql';
FILE: react-app/src/configureClient.ts
type Definintion (line 16) | interface Definintion {
FILE: react-app/src/graphql/mutation/createUser.ts
constant CREATE_USER (line 8) | const CREATE_USER = gql`
FILE: react-app/src/graphql/mutation/updateUser.ts
constant UPDATE_USER (line 8) | const UPDATE_USER = gql`
FILE: react-app/src/graphql/query/login.ts
constant LOGIN_USER (line 8) | const LOGIN_USER = gql`
FILE: react-app/src/graphql/query/user.ts
constant GET_USERS (line 8) | const GET_USERS = gql`
FILE: react-app/src/graphql/subscription/users.ts
constant USER_ADDED (line 8) | const USER_ADDED = gql`
FILE: react-app/src/screens/SignUp/index.tsx
type SignUpState (line 16) | interface SignUpState {
class SignUp (line 23) | class SignUp extends React.PureComponent<any, SignUpState> {
method constructor (line 24) | constructor(props: any) {
method render (line 58) | render() {
FILE: react-app/src/screens/Update/index.tsx
type UpdateState (line 14) | interface UpdateState {
class Update (line 21) | class Update extends React.PureComponent<any, UpdateState> {
method constructor (line 22) | constructor(props: any) {
method render (line 55) | render() {
Condensed preview — 89 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (109K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 52,
"preview": "custom: ['https://www.buymeacoffee.com/anuraggarg']\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/.gitignore",
"chars": 27,
"preview": "node_modules\nyarn.lock\ndist"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/config/express.ts",
"chars": 1543,
"preview": "/**\n * File containing Express Configuration\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport { ApolloServer"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/config/index.ts",
"chars": 323,
"preview": "/**\n * Config file\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport dotenv from 'dotenv';\ndotenv.config();\ne"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/index.ts",
"chars": 1219,
"preview": "/**\n * Bootstrap your app\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport Promise from 'bluebird';\nimport m"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/package.json",
"chars": 1400,
"preview": "{\n \"name\": \"Express_GraphQL_Apollo_Mongodb_Server\",\n \"version\": \"1.0.0\",\n \"description\": \"Backend Server for Next.js "
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/index.ts",
"chars": 430,
"preview": "/**\n * Exporting all resolvers\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport { UserMutation, UserQueries,"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/merge.ts",
"chars": 856,
"preview": "/**\n * Primary file for extracting proper schema structured objects\n * @author Anurag Garg <garganurag893@gmail.com>\n */"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/graphql/resolvers/user.ts",
"chars": 2779,
"preview": "/**\n * File containing all user queries, mutations and subscriptions\n * @author Anurag Garg <garganurag893@gmail.com>\n *"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/graphql/schema/index.ts",
"chars": 1265,
"preview": "/**\n * Primary file for GraphQL Schema\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport { gql } from 'apollo"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/helpers/date.ts",
"chars": 202,
"preview": "/**\n * Define nethod for converting date to string\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nconst dateToStr"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/middleware/auth.ts",
"chars": 767,
"preview": "/**\n * Define middlerware for extracting authToken\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport * as jwt"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/server/models/user.ts",
"chars": 781,
"preview": "/**\n * Define model for user\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport mongoose from 'mongoose';\n\n/**"
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/tsconfig.json",
"chars": 1028,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es6\",\n \"module\": \"commonjs\",\n \"pretty\": true,\n \"sourceMap\": true,\n \""
},
{
"path": "Express_GraphQL_Apollo_Mongodb_Server/tslint.json",
"chars": 580,
"preview": "{\n \"defaultSeverity\": \"error\",\n \"extends\": [\n \"tslint:recommended\"\n ],\n \"rules\": {\n \"no-trailing-whitespace\": "
},
{
"path": "LICENSE",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2020 Anurag Garg\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 7928,
"preview": "<p align=\"center\">\n<a href=\"https://twitter.com/AnuragG94634191\"><img src=\"./Gifs/boilerplate.gif\" title=\"Anurag Garg\" "
},
{
"path": "nextjs-app/.eslintrc.js",
"chars": 410,
"preview": "module.exports = {\n parser: '@typescript-eslint/parser',\n extends: [\n 'plugin:@typescript-eslint/recommende"
},
{
"path": "nextjs-app/.gitignore",
"chars": 277,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "nextjs-app/.prettierrc.js",
"chars": 146,
"preview": "module.exports = {\n useTabs: false,\n printWidth: 80,\n singleQuote: true,\n trailingComma: 'es5',\n jsxBracketSameLine"
},
{
"path": "nextjs-app/next-env.d.ts",
"chars": 75,
"preview": "/// <reference types=\"next\" />\n/// <reference types=\"next/types/global\" />\n"
},
{
"path": "nextjs-app/package.json",
"chars": 1798,
"preview": "{\n \"name\": \"nextjs-graphql-apollo-client\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev"
},
{
"path": "nextjs-app/pages/_app.tsx",
"chars": 897,
"preview": "/**\n * App Configuration\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport App fro"
},
{
"path": "nextjs-app/pages/index.tsx",
"chars": 1713,
"preview": "/**\n * Main Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Login from '../"
},
{
"path": "nextjs-app/pages/signup.tsx",
"chars": 5778,
"preview": "/**\n * SignUp Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Router from '"
},
{
"path": "nextjs-app/pages/subscription.tsx",
"chars": 2148,
"preview": "/**\n * Subscription Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { useSu"
},
{
"path": "nextjs-app/pages/update.tsx",
"chars": 5688,
"preview": "/**\n * Update Profile Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Route"
},
{
"path": "nextjs-app/pages/users.tsx",
"chars": 2457,
"preview": "/**\n * User List Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport List from "
},
{
"path": "nextjs-app/pages/welcome.tsx",
"chars": 1862,
"preview": "/**\n * Welcome Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Card from '."
},
{
"path": "nextjs-app/src/components/Card.tsx",
"chars": 2767,
"preview": "/**\n * Card Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Link from "
},
{
"path": "nextjs-app/src/components/Footer.tsx",
"chars": 976,
"preview": "/**\n * Footer Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\n\nconst Footer: "
},
{
"path": "nextjs-app/src/components/List.tsx",
"chars": 752,
"preview": "/**\n * List Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { UserCard"
},
{
"path": "nextjs-app/src/components/Login.tsx",
"chars": 4922,
"preview": "/**\n * Login Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Router fr"
},
{
"path": "nextjs-app/src/config/index.ts",
"chars": 192,
"preview": "/**\n * Configuration\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nexport const SERVER = 'http://localhost:4020/"
},
{
"path": "nextjs-app/src/configureClient.ts",
"chars": 2349,
"preview": "/**\n * Apollo Client Configuration\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport { ApolloClient } from 'a"
},
{
"path": "nextjs-app/src/graphql/mutation/createUser.ts",
"chars": 293,
"preview": "/**\n * Create User Mutation\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst "
},
{
"path": "nextjs-app/src/graphql/mutation/updateUser.ts",
"chars": 336,
"preview": "/**\n * Update user mutation\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst "
},
{
"path": "nextjs-app/src/graphql/query/login.ts",
"chars": 301,
"preview": "/**\n * Login user query\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst LOGI"
},
{
"path": "nextjs-app/src/graphql/query/user.ts",
"chars": 225,
"preview": "/**\n * Get all users query\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst G"
},
{
"path": "nextjs-app/src/graphql/subscription/users.ts",
"chars": 252,
"preview": "/**\n * New user added subscription\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n"
},
{
"path": "nextjs-app/src/utils/auth.tsx",
"chars": 1151,
"preview": "/**\n * Auth Middlerware Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport * as React from 'react';\n"
},
{
"path": "nextjs-app/src/utils/validation.ts",
"chars": 266,
"preview": "/**\n * Email Validation\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nconst validateEmail = (email: string): boo"
},
{
"path": "nextjs-app/tsconfig.json",
"chars": 532,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es6\",\n \"lib\": [\n \"dom\",\n \"dom.iterable\",\n \"esnext\"\n ],\n "
},
{
"path": "react-app/.eslintrc.js",
"chars": 375,
"preview": "module.exports = {\n parser: '@typescript-eslint/parser',\n extends: [\n 'plugin:@typescript-eslint/recommended',\n "
},
{
"path": "react-app/.gitignore",
"chars": 310,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "react-app/.prettierrc.js",
"chars": 148,
"preview": "module.exports = {\n useTabs: false,\n printWidth: 80,\n singleQuote: true,\n trailingComma: 'es5',\n jsxBracketSameLine"
},
{
"path": "react-app/package.json",
"chars": 2315,
"preview": "{\n \"name\": \"react-app\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"author\": \"Anurag Garg\",\n \"license\": \"MIT\",\n \"depe"
},
{
"path": "react-app/public/index.html",
"chars": 1721,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"%PUBLIC_URL%/favicon.i"
},
{
"path": "react-app/public/manifest.json",
"chars": 332,
"preview": "{\n \"short_name\": \"React GraphQL Boilerplate App\",\n \"name\": \"React GraphQL Boilerplate App\",\n \"icons\": [\n {\n \""
},
{
"path": "react-app/src/App.test.tsx",
"chars": 346,
"preview": "/**\n * Test File\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { render } from"
},
{
"path": "react-app/src/App.tsx",
"chars": 1529,
"preview": "/**\n * App Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { BrowserRo"
},
{
"path": "react-app/src/components/Card/index.tsx",
"chars": 890,
"preview": "/**\n * Card Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport './styles."
},
{
"path": "react-app/src/components/Card/styles.scss",
"chars": 1422,
"preview": ".card {\n display: flex;\n height: 5rem;\n width: 20rem;\n justify-content: center;\n padding: 2rem;\n align"
},
{
"path": "react-app/src/components/Footer/index.tsx",
"chars": 454,
"preview": "/**\n * Footer Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport './style"
},
{
"path": "react-app/src/components/Footer/styles.scss",
"chars": 401,
"preview": ".footer {\n color: rgba(255, 255, 255, 0.5);\n margin-top: auto !important;\n\n\n .footer-text-container {\n f"
},
{
"path": "react-app/src/components/List/index.tsx",
"chars": 504,
"preview": "/**\n * List Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { UserCard"
},
{
"path": "react-app/src/components/List/styles.scss",
"chars": 172,
"preview": ".List {\n &:hove {\n transform: scale(1.1);\n }\n\n h2 {\n font-size: 1rem;\n text-align: right;\n"
},
{
"path": "react-app/src/components/LoginForm/index.tsx",
"chars": 2495,
"preview": "/**\n * Login Form Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { to"
},
{
"path": "react-app/src/components/LoginForm/styles.scss",
"chars": 1588,
"preview": ".login-form {\n display: flex;\n flex-flow: column wrap;\n justify-content: center;\n align-items: center;\n b"
},
{
"path": "react-app/src/config/index.ts",
"chars": 192,
"preview": "/**\n * Configuration\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nexport const SERVER = 'http://localhost:4020/"
},
{
"path": "react-app/src/configureClient.ts",
"chars": 2205,
"preview": "/**\n * Apollo Client Configuration\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport { ApolloClient } from 'a"
},
{
"path": "react-app/src/graphql/mutation/createUser.ts",
"chars": 293,
"preview": "/**\n * Create User Mutation\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst "
},
{
"path": "react-app/src/graphql/mutation/updateUser.ts",
"chars": 336,
"preview": "/**\n * Update user mutation\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst "
},
{
"path": "react-app/src/graphql/query/login.ts",
"chars": 301,
"preview": "/**\n * Login user query\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst LOGI"
},
{
"path": "react-app/src/graphql/query/user.ts",
"chars": 225,
"preview": "/**\n * Get all users query\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n\nconst G"
},
{
"path": "react-app/src/graphql/subscription/users.ts",
"chars": 252,
"preview": "/**\n * New user added subscription\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport gql from 'graphql-tag';\n"
},
{
"path": "react-app/src/index.css",
"chars": 312,
"preview": "h1,\nh2 {\n margin: 0;\n font-family: Candara;\n}\nbody {\n margin: 0;\n padding: 0;\n height: 100%;\n min-height: 100vh;\n "
},
{
"path": "react-app/src/index.tsx",
"chars": 252,
"preview": "/**\n * Primary file for react\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Re"
},
{
"path": "react-app/src/react-app-env.d.ts",
"chars": 40,
"preview": "/// <reference types=\"react-scripts\" />\n"
},
{
"path": "react-app/src/screens/Login/index.tsx",
"chars": 515,
"preview": "/**\n * Main Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport LoginForm from "
},
{
"path": "react-app/src/screens/Login/styles.scss",
"chars": 532,
"preview": ".loginPage-mainContainer {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n\n .loginPage-container {\n "
},
{
"path": "react-app/src/screens/NoMatch/index.tsx",
"chars": 384,
"preview": "/**\n * No Match Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Footer from"
},
{
"path": "react-app/src/screens/NoMatch/styles.scss",
"chars": 268,
"preview": ".nomatch-container {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n display: flex;\n justify-content: center;\n a"
},
{
"path": "react-app/src/screens/SignUp/index.tsx",
"chars": 2882,
"preview": "/**\n * SignUp Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { toast } fro"
},
{
"path": "react-app/src/screens/SignUp/styles.scss",
"chars": 1577,
"preview": ".signup-container {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n display: flex;\n justify-content: ce"
},
{
"path": "react-app/src/screens/Subscription/index.tsx",
"chars": 917,
"preview": "/**\n * Subscription Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { useSu"
},
{
"path": "react-app/src/screens/Subscription/styles.scss",
"chars": 439,
"preview": ".subscription-container {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n display: flex;\n justify-conte"
},
{
"path": "react-app/src/screens/Update/index.tsx",
"chars": 2744,
"preview": "/**\n * Update Profile Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport { toa"
},
{
"path": "react-app/src/screens/Update/styles.scss",
"chars": 1550,
"preview": ".update-container {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n display: flex;\n justify-content: ce"
},
{
"path": "react-app/src/screens/Users/index.tsx",
"chars": 820,
"preview": "/**\n * User List Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport List from "
},
{
"path": "react-app/src/screens/Users/styles.scss",
"chars": 418,
"preview": ".users-container {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n display: flex;\n justify-content: cen"
},
{
"path": "react-app/src/screens/Welcome/index.tsx",
"chars": 624,
"preview": "/**\n * Welcome Page\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport React from 'react';\nimport Card from '."
},
{
"path": "react-app/src/screens/Welcome/styles.scss",
"chars": 416,
"preview": ".welcome-container {\n height: 100%;\n min-height: 100vh;\n width: 100%;\n display: flex;\n justify-content: c"
},
{
"path": "react-app/src/setupTests.ts",
"chars": 255,
"preview": "// jest-dom adds custom jest matchers for asserting on DOM nodes.\n// allows you to do things like:\n// expect(element).to"
},
{
"path": "react-app/src/utils/auth.tsx",
"chars": 742,
"preview": "/**\n * Auth Middlerware Component\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\nimport * as React from 'react';\n"
},
{
"path": "react-app/src/utils/validation.ts",
"chars": 305,
"preview": "/* eslint-disable no-useless-escape */\n/**\n * Email Validation\n * @author Anurag Garg <garganurag893@gmail.com>\n */\n\ncon"
},
{
"path": "react-app/tsconfig.json",
"chars": 491,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\n \"dom\",\n \"dom.iterable\",\n \"esnext\"\n ],\n "
}
]
About this extraction
This page contains the full source code of the garganurag893/Next.js_GraphQL_Express_Apollo_Boilerplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 89 files (95.2 KB), approximately 28.1k tokens, and a symbol index with 61 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.