Skip to content

feat(migration): ✨ add a separate expressjs backend and remove API calls from NextJS API App Router #24

New issue

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

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

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PORT=3000
MONGODB_URI=YOUR_MONGODB_URI
TOKEN_SECRET=YOUR_TOKEN_SECRET
144 changes: 144 additions & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Created by https://www.toptal.com/developers/gitignore/api/node
# Edit at https://www.toptal.com/developers/gitignore?templates=node

### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

### Node Patch ###
# Serverless Webpack directories
.webpack/

# Optional stylelint cache

# SvelteKit build / generate output
.svelte-kit

# End of https://www.toptal.com/developers/gitignore/api/node
28 changes: 28 additions & 0 deletions server/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import express from "express";
import cors from "cors";
import morgan from "morgan";
import cookieParser from "cookie-parser";

// Importing all the routes
import signupRoute from "./routes/signupRoute.ts";
import loginRoute from "./routes/loginRoute.ts";
import logoutRoute from "./routes/logoutRoute.ts";
import usersRoute from "./routes/usersRoute.ts";

const app = express();

// setting up the middlewares
app.use(express.json());
app.use(cookieParser());
app.use(cors());
app.use(morgan("dev"));
if (process.env.NODE_ENV === "development") {
app.use(morgan("dev"));
}

app.use("/api/v1/users/signup", signupRoute);
app.use("/api/v1/users/login", loginRoute);
app.use("/api/v1/users/logout", logoutRoute);
app.use("/api/v1/users", usersRoute);

export { app };
46 changes: 46 additions & 0 deletions server/controllers/loginController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import User from "../models/UserModel.ts";
import { Request, Response } from "express";

const loginController = async (req: Request, res: Response) => {
try {
const { username, password } = req.body;

if (!username || !password) {
return res
.status(400)
.json({ message: "Please fill out all fields" });
}

const user = await User.findOne({ username });
if (!user) {
return res.status(400).json({ message: "User does not exist!" });
}

const checkPassword = await bcrypt.compare(password, user.password);
if (!checkPassword) {
return res
.status(403)
.json({ message: "Incorrect Password!", success: false });
}

const tokenData = {
id: user._id,
username: user.username,
};

const token = await jwt.sign(tokenData, process.env.TOKEN_SECRET!);

console.log(token);

res.cookie("token", token).status(200).json({
message: "User logged in successfully",
success: true,
});
} catch (error) {
res.status(500).json({ error, message: "Error logging in user" });
}
};

export default loginController;
16 changes: 16 additions & 0 deletions server/controllers/logoutController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Request, Response } from "express";

const logoutController = (req: Request, res: Response) => {
try {
res.cookie("token", "").status(200).json({
message: "User logged out successfully",
success: true,
});
} catch (error) {
return res
.status(500)
.json({ error, message: "Error logging out user" });
}
};

export default logoutController;
52 changes: 52 additions & 0 deletions server/controllers/signupController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import bcrypt from "bcrypt";
import User from "../models/UserModel.ts";
import { Request, Response } from "express";

const signupController = async (req: Request, res: Response) => {
try {
const { username, email, password } = req.body;

if (!username || !email || !password) {
return res
.status(400)
.json({ message: "Please fill out all fields" });
}

const checkEmail = await User.findOne({ email });
if (checkEmail) {
return res
.status(400)
.json({ message: "Email already exists in the database!" });
}

const checkUsername = await User.findOne({ username });
if (checkUsername) {
return res
.status(400)
.json({ message: "Username already exists!" });
}

const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);

const newUser = new User({
email,
username,
password: hashedPassword,
});

const savedUser = await newUser.save();

return res.status(201).json({
message: "User created successfully",
success: true,
savedUser,
});
} catch (error) {
return res
.status(500)
.json({ error, message: "Error signing up user" });
}
};

export default signupController;
50 changes: 50 additions & 0 deletions server/controllers/usersController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Request, Response } from "express";
import jwt from "jsonwebtoken";
import User from "../models/UserModel.ts";

const usersController = async (req: Request, res: Response) => {
try {
const { username } = req.body;

const token = req.cookies?.token;

if (!username) {
res.status(400).json({
message: "No username revieved!",
});
}

let isSelf = false;
let doesExist = false;

let data: any;

if (token) {
data = jwt.verify(token, process.env.TOKEN_SECRET!);
}

if (data?.username === username) {
isSelf = true;
}

let user = await User.findOne({ username });
if (user) {
user = user.toObject();
delete user.password;
doesExist = true;
}

res.status(200).json({
isSelf,
doesExist,
user,
token,
});
} catch (error: any) {
res.status(500).json({
message: error.message,
});
}
};

export default usersController;
49 changes: 49 additions & 0 deletions server/models/UserModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import mongoose from "mongoose";

const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, "Please provide an email!"],
unique: true,
lowercase: true,
},
username: {
type: String,
required: [true, "Please provide a username!"],
unique: true,
lowercase: true,
},
password: {
type: String,
required: [true, "Please provide a password!"],
minlength: 8,
},
isVerified: {
type: Boolean,
default: false,
},
name: {
type: String,
required: false,
},
bio: {
type: String,
required: false,
},
socials: {
email: String,
github: String,
twitter: String,
instagram: String,
linkedin: String,
web: String,
},
tags: ["Founder", "First 100", "Dev"],
button: {
text: String,
link: String,
},
});

const User = mongoose.models.User || mongoose.model("User", userSchema);
export default User;
Loading