diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..7a8620bea --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-slim + +WORKDIR /usr/src/app + +# Install dependencies first to leverage Docker layer caching +COPY package*.json ./ +RUN npm install --omit=dev + +COPY . . + +EXPOSE 3000 + +CMD ["npm", "start"] diff --git a/Note.js b/Note.js new file mode 100644 index 000000000..07d38bc91 --- /dev/null +++ b/Note.js @@ -0,0 +1,11 @@ +const mongoose = require('mongoose'); + +const noteSchema = new mongoose.Schema( + { + title: { type: String, required: true }, + content: { type: String, default: '' }, + }, + { timestamps: true } +); + +module.exports = mongoose.model('Note', noteSchema); diff --git a/README.md b/README.md index 86f9adc2b..21cca9126 100644 --- a/README.md +++ b/README.md @@ -1,121 +1,79 @@ -# Awesome Compose [![Awesome](https://awesome.re/badge.svg)](https://awesome.re) - -![logo](awesome-compose.jpg) - -> A curated list of Docker Compose samples. - -These samples provide a starting point for how to integrate different services using a Compose file and to manage their deployment with Docker Compose. - -> **Note** -> The following samples are intended for use in local development environments such as project setups, tinkering with software stacks, etc. These samples must not be deployed in production environments. - - -## Contents - -- [Samples of Docker Compose applications with multiple integrated services](#samples-of-docker-compose-applications-with-multiple-integrated-services). -- [Single service samples](#single-service-samples). -- [Basic setups for different platforms (not production ready - useful for personal use)](#basic-setups-for-different-platforms-not-production-ready---useful-for-personal-use). - -## Samples of Docker Compose applications with multiple integrated services - -Docker + wasm Icon indicates Sample is compatible with [Docker+Wasm](https://docs.docker.com/desktop/wasm/). - -- [`ASP.NET / MS-SQL`](aspnet-mssql) - Sample ASP.NET core application -with MS SQL server database. -- [`Elasticsearch / Logstash / Kibana`](elasticsearch-logstash-kibana) - Sample Elasticsearch, Logstash, and Kibana stack. -- [`Go / NGINX / MySQL`](nginx-golang-mysql) - Sample Go application -with an Nginx proxy and a MySQL database. -- [`Go / NGINX / PostgreSQL`](nginx-golang-postgres) - Sample Go -application with an Nginx proxy and a PostgreSQL database. -- [`Java Spark / MySQL`](sparkjava-mysql) - Sample Java application and -a MySQL database. -- [`NGINX / ASP.NET / MySQL`](nginx-aspnet-mysql) - Sample Nginx reverse proxy with an C# backend using ASP.NET. -- [`NGINX / Flask / MongoDB`](nginx-flask-mongo) - Sample Python/Flask -application with Nginx proxy and a Mongo database. -- [`NGINX / Flask / MySQL`](nginx-flask-mysql) - Sample Python/Flask application with an Nginx proxy and a MySQL database. -- [`NGINX / Node.js / Redis`](nginx-nodejs-redis) - Sample Node.js application with Nginx proxy and a Redis database. -- [`NGINX / Go`](nginx-golang) - Sample Nginx proxy with a Go backend. -- [`NGINX / WSGI / Flask`](nginx-wsgi-flask) - Sample Nginx reverse proxy with a Flask backend using WSGI. -- [`PostgreSQL / pgAdmin`](postgresql-pgadmin) - Sample setup for postgreSQL database with pgAdmin web interface. -- [`Python / Flask / Redis`](flask-redis) - Sample Python/Flask and a Redis database. -- [`React / Spring / MySQL`](react-java-mysql) - Sample React -application with a Spring backend and a MySQL database. -- [`React / Express / MySQL`](react-express-mysql) - Sample React -application with a Node.js backend and a MySQL database. -- [`React / Express / MongoDB`](react-express-mongodb) - Sample React -application with a Node.js backend and a Mongo database. -- [`React / Rust / PostgreSQL`](react-rust-postgres) - Sample React -application with a Rust backend and a Postgres database. -- [`React / Nginx`](react-nginx) - Sample React application with Nginx. -- [`Spring / PostgreSQL`](spring-postgres) - Sample Java application -with Spring framework and a Postgres database. -- [`WasmEdge / MySQL / Nginx`](wasmedge-mysql-nginx) - Sample Wasm-based web application with a static HTML frontend, using a MySQL (MariaDB) database. The frontend connects to a Wasm microservice written in Rust, that runs using the WasmEdge runtime. Compatible with Docker+wasm -- [`WasmEdge / Kafka / MySQL`](wasmedge-kafka-mysql) - Sample Wasm-based microservice that subscribes to a Kafka (Redpanda) queue topic, and transforms and saves any incoming message into a MySQL (MariaDB) database. Compatible with Docker+wasm - -## Single service samples - -- [`Angular`](angular) -- [`Spark`](sparkjava) -- [`VueJS`](vuejs) -- [`Flask`](flask) -- [`PHP`](apache-php) -- [`Traefik`](traefik-golang) -- [`Django`](django) -- [`Minecraft server`](https://github.com/docker/awesome-compose/tree/master/minecraft) -- [`Plex`](https://github.com/docker/awesome-compose/tree/master/plex) -- [`Portainer`](https://github.com/docker/awesome-compose/tree/master/portainer) -- [`Wireguard`](https://github.com/docker/awesome-compose/tree/master/wireguard) -- [`FastAPI`](fastapi) - -## Basic setups for different platforms (not production ready - useful for personal use) - -- [`Gitea / PostgreSQL`](gitea-postgres) -- [`Nextcloud / PostgreSQL`](nextcloud-postgres) -- [`Nextcloud / Redis / MariaDB`](nextcloud-redis-mariadb) -- [`Pi-hole / cloudflared`](pihole-cloudflared-DoH) - Sample Pi-hole setup with use of DoH cloudflared service -- [`Prometheus / Grafana`](prometheus-grafana) -- [`Wordpress / MySQL`](wordpress-mysql) - - - -## Getting started - -These instructions will get you through the bootstrap phase of creating and -deploying samples of containerized applications with Docker Compose. - -### Prerequisites - -- Make sure that you have Docker and Docker Compose installed - - Windows or macOS: - [Install Docker Desktop](https://www.docker.com/get-started) - - Linux: [Install Docker](https://www.docker.com/get-started) and then - [Docker Compose](https://github.com/docker/compose) -- Download some or all of the samples from this repository. - -### Running a sample - -The root directory of each sample contains the `compose.yaml` which -describes the configuration of service components. All samples can be run in -a local environment by going into the root directory of each one and executing: - -```console -docker compose up -d +# Express application with MongoDB database + +## App diagram + +The compose file defines an application with two services: `backend` and `mongo`. + +When deploying the application, `docker compose` maps port 3000 of the `backend` +service container to port 3000 of the host as specified in the `compose.yaml` +file. + +Make sure port 3000 on the host is not already in use. + +## Deploy with docker compose + +```shell +$ docker compose up -d +``` + +Expected result: listing containers must show two containers running and +healthy. Healthcheck isn't enabled by default, but the logs should show the +server listening and connected to MongoDB. + +```shell +$ docker compose ps + +NAME IMAGE COMMAND SERVICE STATUS +backend express-mongodb-backend "docker-entrypoint.s…" backend running +mongo mongo:7 "docker-entrypoint.s…" mongo running +``` + +After the application starts, navigate to `http://localhost:3000` in your web +browser or run: + +```shell +$ curl http://localhost:3000 +{"message":"Express + MongoDB API is running"} +``` + +### Try the API + +Create a note: + +```shell +$ curl -X POST http://localhost:3000/notes \ + -H "Content-Type: application/json" \ + -d '{"title": "First note", "content": "Hello from MongoDB"}' +``` + +List notes: + +```shell +$ curl http://localhost:3000/notes ``` -Check the `README.md` of each sample to get more details on the structure and -what is the expected output. -To stop and remove all containers of the sample application run: +Stop and remove the containers: -```console -docker compose down +```shell +$ docker compose down ``` -### Quickstart guides +## Expected result + +Listing containers must show two containers running and healthy: -In addition to all the ready to run Compose samples listed above the folder [official-documentation-samples](official-documentation-samples/README.md) contains quickstart guides. Each of these step by step guides explain which files need to be created to build and run a Docker Compose application. +```shell +$ docker compose ps +NAME IMAGE COMMAND SERVICE STATUS +backend express-mongodb-backend "docker-entrypoint.s…" backend running +mongo mongo:7 "docker-entrypoint.s…" mongo running +``` + +After the application starts, you can use the API endpoints described above +to create, list, update, and delete notes stored in MongoDB. - -## Contribute +Stop and remove the containers with: -We welcome examples that help people understand how to use Docker Compose for -common applications. Check the [Contribution Guide](CONTRIBUTING.md) for more details. +```shell +$ docker compose down +``` diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..f82844976 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,25 @@ +services: + backend: + build: + context: backend + container_name: backend + restart: always + ports: + - 3000:3000 + environment: + NODE_PORT: 3000 + MONGO_URL: mongodb://mongo:27017/notesdb + depends_on: + - mongo + + mongo: + image: mongo:7 + container_name: mongo + restart: always + volumes: + - db-data:/data/db + ports: + - 27017:27017 + +volumes: + db-data: diff --git a/notes.js b/notes.js new file mode 100644 index 000000000..fcebe1d43 --- /dev/null +++ b/notes.js @@ -0,0 +1,50 @@ +const express = require('express'); +const Note = require('../models/Note'); + +const router = express.Router(); + +// GET /notes - list all notes +router.get('/', async (req, res) => { + const notes = await Note.find().sort({ createdAt: -1 }); + res.json(notes); +}); + +// POST /notes - create a note +router.post('/', async (req, res) => { + try { + const note = await Note.create({ + title: req.body.title, + content: req.body.content, + }); + res.status(201).json(note); + } catch (err) { + res.status(400).json({ error: err.message }); + } +}); + +// GET /notes/:id - get a single note +router.get('/:id', async (req, res) => { + const note = await Note.findById(req.params.id); + if (!note) return res.status(404).json({ error: 'Note not found' }); + res.json(note); +}); + +// PUT /notes/:id - update a note +router.put('/:id', async (req, res) => { + const note = await Note.findByIdAndUpdate( + req.params.id, + { title: req.body.title, content: req.body.content }, + { new: true, runValidators: true } + ); + if (!note) return res.status(404).json({ error: 'Note not found' }); + res.json(note); +}); + +// DELETE /notes/:id - delete a note +router.delete('/:id', async (req, res) => { + const note = await Note.findByIdAndDelete(req.params.id); + if (!note) return res.status(404).json({ error: 'Note not found' }); + res.status(204).send(); +}); + +module.exports = router; diff --git a/package.json b/package.json new file mode 100644 index 000000000..c0d8512ee --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "express-mongodb-backend", + "version": "1.0.0", + "description": "Sample Express REST API backed by MongoDB", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "express": "^4.19.2", + "mongoose": "^8.4.1" + } +} diff --git a/server.js b/server.js new file mode 100644 index 000000000..a164b59bb --- /dev/null +++ b/server.js @@ -0,0 +1,28 @@ +const express = require('express'); +const mongoose = require('mongoose'); +const notesRouter = require('./routes/notes'); + +const app = express(); +const PORT = process.env.NODE_PORT || 3000; +const MONGO_URL = process.env.MONGO_URL || 'mongodb://mongo:27017/notesdb'; + +app.use(express.json()); + +app.get('/', (req, res) => { + res.json({ message: 'Express + MongoDB API is running' }); +}); + +app.use('/notes', notesRouter); + +mongoose + .connect(MONGO_URL) + .then(() => { + console.log('Connected to MongoDB'); + app.listen(PORT, () => { + console.log(`Server listening on port ${PORT}`); + }); + }) + .catch((err) => { + console.error('Failed to connect to MongoDB', err); + process.exit(1); + });