Aakaar is a full-stack Canva-style design editor built with a Next.js frontend and Node.js backend microservices. It supports canvas-based editing, project persistence, image uploads, AI image generation, authentication, and a Docker-based backend deployment workflow.
This project demonstrates a production-style full-stack architecture for an online design editor. The frontend provides the editor, dashboard, authentication flow, and subscription UI. The backend is split into focused services behind an API gateway and Nginx reverse proxy.
- Canva-like editor with shapes, text, images, drawing tools, and object controls
- Design dashboard with create, load, update, and delete workflows
- Auto-save support for design data
- Image upload flow using Cloudinary
- AI image generation integration
- Export designs as PNG, JPG, SVG, and JSON
- Google authentication support
- Premium/subscription flow with PayPal integration
- Dockerized backend services for local and cloud deployment
- CI/CD pipeline that builds Docker images, pushes to Docker Hub, and deploys to EC2/Lightsail
- Frontend: Next.js App Router, React, TailwindCSS, Shadcn UI
- Canvas: Fabric.js
- State Management: Zustand
- Authentication: Auth.js / NextAuth v5, Google OAuth
- Backend: Node.js, Express, API Gateway, microservices
- Database: MongoDB / MongoDB Atlas
- Storage: Cloudinary
- Payments: PayPal subscriptions
- DevOps: Docker, Docker Compose, Nginx, GitHub Actions, Docker Hub, EC2/Lightsail
flowchart LR
User[User Browser] --> Frontend[Next.js Client]
Frontend --> Gateway[Nginx / API Gateway]
Gateway --> Design[Design Service]
Gateway --> Upload[Upload Service]
Gateway --> Subscription[Subscription Service]
Design --> Mongo[(MongoDB)]
Upload --> Mongo
Subscription --> Mongo
Upload --> Cloudinary[Cloudinary]
Upload --> Stability[AI Image API]
Subscription --> PayPal[PayPal]
GitHub[GitHub Actions] --> DockerHub[Docker Hub]
DockerHub --> Server[EC2 / Lightsail Docker Compose]
Aakaar follows a split frontend/backend architecture. The Next.js client handles the user interface, editor experience, authentication screens, and dashboard. Backend traffic goes through Nginx and the API Gateway, which routes requests to dedicated services for design data, media uploads, and subscriptions.
The design and upload services persist application data in MongoDB. The upload service also integrates with Cloudinary for asset storage and an external AI image API for image generation. The subscription service is designed for PayPal billing workflows. In production, the backend services run as Docker containers on EC2/Lightsail and are updated through GitHub Actions and Docker Hub.
client/ Next.js frontend
server/ Backend services and Docker Compose files
server/api-gateway/ API gateway service
server/design-service/ Design CRUD service
server/upload-service/ Upload and AI image service
server/subscription-service/ Subscription service
server/nginx/ Nginx reverse proxy config
.github/workflows/deploy.yaml Backend CI/CD workflow
cd client
npm install
npm run devFrontend runs at:
http://localhost:3000
Useful frontend environment values:
NEXT_PUBLIC_API_MODE=docker
NEXT_PUBLIC_LOCAL_API_URL=http://localhost:5000
NEXT_PUBLIC_DOCKER_API_URL=http://localhost:8080
NEXT_PUBLIC_API_URL=NEXT_PUBLIC_API_URL overrides the local/docker mode values and is useful when pointing the deployed frontend to a deployed backend.
Each backend service is a separate Node.js app. For most development, Docker Compose is recommended because it starts the services together with consistent networking.
All backend Docker files are inside server/.
The development compose file includes local MongoDB.
cd server
cp .env.dev.example .env.dev
docker compose -f docker-compose.dev.yml --env-file .env.dev up -d --buildBackend entrypoint:
http://localhost:8080
Stop development containers:
docker compose -f docker-compose.dev.yml --env-file .env.dev downThe production compose file expects a cloud MongoDB connection string.
cd server
cp .env.example .env
# Fill the required values in .env
docker compose -f docker-compose.yml up -d --buildStop production containers:
docker compose -f docker-compose.yml downConfigure these in server/.env for deployment:
MONGO_URI- MongoDB connection stringGOOGLE_CLIENT_ID- Google OAuth client IDGOOGLE_CLIENT_SECRET- Google OAuth client secretCORS_ORIGINS- comma-separated allowed frontend originsSTABILITY_API_KEY- AI image generation API keycloud_name- Cloudinary cloud nameapi_key- Cloudinary API keyapi_secret- Cloudinary API secretDOCKERHUB_USERNAME- Docker Hub namespace used by Docker Compose image namesIMAGE_TAG- Docker image tag to run, usually set by CI/CD
- API Gateway:
5000 - Design Service:
5001 - Upload Service:
5002 - Subscription Service:
5003 - Nginx public entrypoint:
8080 -> 80
- Frontend:
https://aakaar-alpha.vercel.app - Backend: configure your EC2/Lightsail public IP or API domain, for example
https://api.yourdomain.com
For production, set the frontend environment variable:
NEXT_PUBLIC_API_URL=https://api.yourdomain.comAlso set backend CORS:
CORS_ORIGINS=https://aakaar-alpha.vercel.appThe backend deployment workflow is defined in .github/workflows/deploy.yaml.
On every push to main, GitHub Actions:
- Checks out the repository.
- Logs in to Docker Hub using GitHub secrets.
- Builds Docker images for:
api-gatewaydesign-serviceupload-service
- Pushes each image to Docker Hub with two tags:
- the commit SHA, for traceable deployments
latest, for convenience
- SSHes into the EC2/Lightsail server.
- Pulls the latest repository code.
- Pulls the newly built Docker images.
- Stops the current containers.
- Starts the Docker Compose stack with the new images.
- Runs
docker image prune -fto remove dangling unused image layers and reduce disk usage.
Required GitHub repository secrets:
DOCKERHUB_USERNAMEDOCKERHUB_TOKENHOSTUSERNAMESSH_KEY
The server should have the repository checked out at:
~/canvaclone
The deploy job runs the equivalent of:
cd ~/canvaclone
git pull origin main
cd server
docker compose pull
docker compose down
docker compose up -d
docker image prune -fThis project uses a build-on-CI, run-on-server strategy:
- GitHub Actions builds the Docker images.
- Docker Hub stores the images.
- EC2/Lightsail pulls and runs the images.
This is usually better than building directly on the server because the server stays lighter, deployments are more repeatable, and each release can be traced back to a commit SHA.
After the backend is running:
curl http://localhost:8080/healthExpected response: JSON health status from the gateway.
From server/:
# See running containers
docker compose ps
# Follow logs
docker compose logs -f
# Rebuild and restart locally
docker compose up -d --build
# Pull pushed images and restart
docker compose pull
docker compose down
docker compose up -d
# Remove dangling unused images
docker image prune -f- If the frontend gets CORS errors, update
CORS_ORIGINS. - If auth fails, verify
GOOGLE_CLIENT_IDand Google OAuth settings. - If upload or AI routes fail, verify Cloudinary and Stability API keys.
- If Docker Compose warns that
MONGO_URIis empty, fill it inserver/.env. - If EC2 runs out of disk space, check old Docker images with
docker imagesand unused data withdocker system df.