DEV Community

Parth Kathrotiya
Parth Kathrotiya

Posted on

Full Next.js + Node.js + PostgreSQL Interview Task Setup

Tech Stack:
Frontend: Next.js

  • Backend: Node.js + Express.js
  • Database: PostgreSQL
  • ORM: Sequelize
  • Interview technical round project Flow
  1. Create Frontend
    npx create-next-app@latest frontend
    touch .env

  2. Create Backend & Install Backend Dependencies
    mkdir backend
    cd backend
    npm init -y
    npm install express cors dotenv bcryptjs jsonwebtoken pg pg-hstore sequelize
    npm install -D nodemon sequelize-cli

  3. Backend Folder Structure Commands
    mkdir config controllers middleware models routes migrations seeders
    touch server.js
    touch config/db.js
    touch middleware/authMiddleware.js
    touch controllers/authController.js
    touch controllers/*.js
    touch controllers/*.js
    touch routes/authRoutes.js
    touch routes/*.js
    touch routes/*.js
    touch .env

    Frontend Dependencies
    cd frontend
    npm install axios react-hot-toast
    touch .env

    If want to setup Tailwind go to tailwind site and chose v3 and do the setup.

  4. package.json Scripts
    "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
    }

  5. Sequelize Initialization & PostgreSQL Database Creation
    npx sequelize-cli init
    This creates:
    config/
    models/
    migrations/
    seeders/

    Open PostgreSQL terminal:
    CREATE DATABASE employee_management;

  6. Backend & Frontend .env File
    Backend:

PORT=5000
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=yourpassword
DB_NAME=employee_management
JWT_SECRET=mysecretkey
Enter fullscreen mode Exit fullscreen mode

Frontend:
NEXT_PUBLIC_BASE_URL=http://localhost:5000
Sequelize Config for PostgreSQL
config/config.json

{
  "development": {
    "username": "postgres",
    "password": "",
    "database": "",
    "host": "localhost",
    "dialect": "postgres"
  }
}

Enter fullscreen mode Exit fullscreen mode
  1. Database Connection config/db.js
const { Sequelize } = require("sequelize");
require("dotenv").config();

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    dialect: "postgres",
    logging: false,
  }
);

module.exports = sequelize;
Enter fullscreen mode Exit fullscreen mode
  1. Generate Models and Migrations & Run Migration
    User Model
    npx sequelize-cli model:generate --name User --attributes name:string,email:string,password:string
    Run Migration
    npx sequelize-cli db:migrate

  2. Express Server Setup
    server.js

const express = require("express");
const cors = require("cors");

require("dotenv").config();

const app = express();

app.use(cors());

app.use(express.json());

app.use("/api/auth", require("./routes/authRoutes"));

app.use("/api/employees", require("./routes/employeeRoutes"));

app.use("/api/attendance", require("./routes/attendanceRoutes"));

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`Server running on ${PORT}`);
});

Enter fullscreen mode Exit fullscreen mode
  1. Authentication Middleware middleware/authMiddleware.js
const jwt = require("jsonwebtoken");

module.exports = (req, res, next) => {
  try {
    const token = req.headers.authorization?.split(" ")[1];

    if (!token) {
      return res.status(401).json({
        message: "No token provided",
      });
    }

    const decoded = jwt.verify(
      token,
      process.env.JWT_SECRET
    );

    req.user = decoded;

    next();
  } catch (error) {
    res.status(401).json({
      message: "Invalid token",
    });
  }
};

Enter fullscreen mode Exit fullscreen mode
  1. Login Controller controllers/authController.js
const bcrypt = require("bcryptjs");

const jwt = require("jsonwebtoken");

const { User } = require("../models");

exports.login = async (req, res) => {
  try {
    const { email, password } = req.body;

    const user = await User.findOne({
      where: { email },
    });

    if (!user) {
      return res.status(400).json({
        message: "User not found",
      });
    }

    const isMatch = await bcrypt.compare(
      password,
      user.password
    );

    if (!isMatch) {
      return res.status(400).json({
        message: "Invalid credentials",
      });
    }

    const token = jwt.sign(
      { id: user.id },
      process.env.JWT_SECRET,
      {
        expiresIn: "1d",
      }
    );

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

Enter fullscreen mode Exit fullscreen mode
  1. Auth Routes routes/authRoutes.js
const router = require("express").Router();

const controller = require("../controllers/authController");

router.post("/login", controller.login);

module.exports = router;

12. Axios Setup
src/utils/axios.js
import axios from "axios";

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_BASE_URL,
});

api.interceptors.request.use((config) => {
  const token = localStorage.getItem("token");

  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});

export default api;

Enter fullscreen mode Exit fullscreen mode
  1. React Router Setup src/app/App.js
import {
  createBrowserRouter,
  Navigate,
  RouterProvider 
} from "react-router-dom";
import React from "react";
import ReactDOM from "react-dom/client";

import Login from "./pages/Login";
import Register from "./pages/Register";
import Home from "./pages/Home";
import Profile from "./pages/Profile";

function PrivateRoute({ children }) {

  const isLoggedIn = localStorage.getItem("token");

  return isLoggedIn
    ? children
    : <Navigate to="/login" />;
}

function PublicRoute({ children }) {

  const isLoggedIn = localStorage.getItem("token");

  return isLoggedIn
    ? <Navigate to="/" />
    : children;
}

const router = createBrowserRouter([
  {
    path: "/login",
    element: (
      <PublicRoute>
        <Login />
      </PublicRoute>
    ),
  },

  {
    path: "/register",
    element: (
      <PublicRoute>
        <Register />
      </PublicRoute>
    ),
  },

  {
    path: "/",
    element: (
      <PrivateRoute>
        <Home />
      </PrivateRoute>
    ),
  },

  {
    path: "/profile",
    element: (
      <PrivateRoute>
        <Profile />
      </PrivateRoute>
    ),
  },
]);

function App() {
  return (
    <div>
    <RouterProvider router={router} />
    </div>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode
  1. Login API Example src/app/login/login.js
import api from "@/utils/axios";
import { useRouter } from "next/navigation";

const router = useRouter();

const login = async () => {
  const res = await api.post("/auth/login", {
    email,
    password,
  });

  localStorage.setItem(
    "token",
    res.data.token
  );

router.push("/");
};

Enter fullscreen mode Exit fullscreen mode
  1. Logout Function
import { useRouter } from "next/navigation";

const router = useRouter();
const logout = () => {
  localStorage.removeItem("token");

  router.push("/login");
};

Enter fullscreen mode Exit fullscreen mode

EXAMPLES

  1. Next.js Routing src/app/login/page.js

src/app/dashboard/page.js

src/app/employees/page.js

  1. Redirect After Login import { useRouter } from "next/navigation";

const router = useRouter();

router.push("/dashboard");

  1. Important PostgreSQL Commands Open PostgreSQL psql -U postgres

Show Databases
\l

Connect Database
\c employee_management

Show Tables
\dt

  1. Migration Commands Run Migration npx sequelize-cli db:migrate

Undo Last Migration
npx sequelize-cli db:migrate:undo

Undo All Migration
npx sequelize-cli db:migrate:undo:all

  1. Raw Query Example const sequelize = require("../config/db");

const [employees] = await sequelize.query(
'SELECT * FROM "Employees"'
);

console.log(employees);

  1. Sequelize Query Examples Create await Employee.create({ name: "Parth", email: "parth@gmail.com", });

Find All
await Employee.findAll();

Find One
await Employee.findOne({
where: {
id: 1,
},
});

Update
await Employee.update(
{
name: "Updated",
},
{
where: {
id: 1,
},
}
);

Delete
await Employee.destroy({
where: {
id: 1,
},
});

  1. Example Migration File migrations/XXXXXXXXXXXX-create-user.js "use strict";

module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Users", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},

  name: {
    type: Sequelize.STRING,
  },

  email: {
    type: Sequelize.STRING,
    unique: true,
  },

  password: {
    type: Sequelize.STRING,
  },

  createdAt: {
    allowNull: false,
    type: Sequelize.DATE,
  },

  updatedAt: {
    allowNull: false,
    type: Sequelize.DATE,
  },
});
Enter fullscreen mode Exit fullscreen mode

},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable("Users");
},
};

  1. Example Model File models/user.js "use strict";

const { Model } = require("sequelize");

module.exports = (sequelize, DataTypes) => {
class User extends Model {
static associate(models) {}
}

User.init(
{
name: DataTypes.STRING,

  email: DataTypes.STRING,

  password: DataTypes.STRING,
},
{
  sequelize,
  modelName: "User",
}
Enter fullscreen mode Exit fullscreen mode

);

return User;
};

  1. Importing Models Usage const db = require("../models"); const { User, Employee, Attendance } = db;

Tailwind setup Core React

  1. Install Tailwind CSS v3
    npm install -D tailwindcss@3

  2. Create Tailwind Config File
    npx tailwindcss init
    This creates:
    tailwind.config.js

  3. Configure Template Paths
    Inside tailwind.config.js
    /** @type {import('tailwindcss').Config} /
    export default {
    content: ["./src/
    /.{html,js,jsx}"],
    theme: {
    extend: {},
    },
    plugins: [],
    }

  4. Add into index CSS File
    Create:
    src/index.css
    Add:
    @tailwind base;
    @tailwind components;
    @tailwind utilities;

  5. Start Tailwind Build Process
    Run:
    npx tailwindcss -i ./src/input.css -o ./src/output.css --watch
    This command:
    Reads Tailwind classes from input.css
    Generates final CSS into output.css
    Watches for live changes

Top comments (0)