Skip to content
Open
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
60 changes: 37 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
# get started

```
git clone https://github.com/mk48/fullstack-crud-react-node-postgresql.git

cd webclient
yarn
cd..
cd server
npm install
```

## start server
```
cd server
SET DEBUG=server:* & npm start
```

## start web
```
cd webclient
yarn start
```
# [Dockerization of the full stack project]

Full-stack application with **Dockerized** frontend, backend, and PostgreSQL database.

Modern, containerized setup — ready to run with one command (if you have `docker compose`).

## Tech Stack

- **Frontend**: [React / Next.js / Vite + React / Vue / Angular / ...]
- **Backend**: [Node.js + Express / NestJS / Spring Boot / FastAPI / Django / Go / ...]
- **Database**: PostgreSQL
- **Containerization**: Docker + Docker Compose (recommended)

## Features

- Completely containerized (frontend, backend, db)
- Easy local development & deployment
- Persistent PostgreSQL data (via volume)
- Frontend available at http://localhost:3000

### Docker Hub – Frontend & Backend images

![Docker Hub images](screenshots/image1.png)
![Docker Hub Frontend image ](screenshots/image2.png)

### Running containers (`docker ps`)

![docker ps output](screenshots/image4.png)

### Frontend running at http://localhost:3000

![Frontend running](screenshots/image3.png)

## Prerequisites

- Docker 20+
- Docker Compose (included in Docker Desktop / can be installed separately)
48 changes: 48 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
version: "3.9"

services:
postgres:
image: postgres:14
container_name: postgres
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: crud_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data

backend:
build: ./server
container_name: backend
restart: always
ports:
- "5000:5000"
depends_on:
- postgres
environment:
DB_HOST: postgres
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAME: crud_db
volumes:
- ./server:/usr/src/app

frontend:
build: ./webclient
container_name: frontend
restart: always
ports:
- "3000:3000"
depends_on:
- backend
environment:
- NODE_OPTIONS=--openssl-legacy-provider
volumes:
- ./webclient:/app
- /app/node_modules

volumes:
postgres_data:
Binary file added screenshots/image1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/image2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/image3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/image4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions server/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DB_HOST=localhost
DB_USER=devuser
DB_PASSWORD=devpass
DB_NAME=crudapp
DB_PORT=5432
PORT=5000
19 changes: 19 additions & 0 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:16-alpine

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install --production

COPY . .

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser


EXPOSE 5000

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s \
CMD curl -f http://localhost:5000/ || exit 1

CMD ["npm", "start"]
76 changes: 59 additions & 17 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,61 @@ var userRouter = require('./routes/user');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
/* ========================
BASIC MIDDLEWARE
======================== */

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use(function(req, res, next) {
/* ========================
CORS CONFIGURATION
======================== */

app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, x-access-token");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, x-access-token, Authorization"
);
res.header(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS"
);

if (req.method === "OPTIONS") {
return res.sendStatus(200);
}

next();
});

/* ========================
HEALTH & ROOT ROUTES
======================== */

// Health check for Docker
app.get('/health', (req, res) => {
res.status(200).json({
status: "OK",
message: "Server is healthy"
});
});

// Root route (prevents 404 on /)
app.get('/', (req, res) => {
res.status(200).json({
message: "API is running successfully 🚀"
});
});

/* ========================
API ROUTES
======================== */

app.use('/products', productRouter);
app.use('/category', categoryRouter);
app.use('/report', reportRouter);
Expand All @@ -38,20 +77,23 @@ app.use('/purchase', purchaseRouter);
app.use('/admin', adminRouter);
app.use('/user', userRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
/* ========================
404 HANDLER
======================== */

app.use((req, res, next) => {
next(createError(404, "Route Not Found"));
});

// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
/* ========================
ERROR HANDLER
======================== */

// render the error page
res.status(err.status || 500);
res.render('error');
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
success: false,
message: err.message || "Internal Server Error"
});
});

module.exports = app;
module.exports = app;
Loading