# mern-crud: a MongoDB records app that broadcasts writes over Socket.io

> mern-crud is a small JavaScript stack that puts an Express REST API, a MongoDB model layer, a React front end and Socket.io broadcasts into one repository, so a create or delete on the server reaches every open browser without a refresh. It is a teaching-sized codebase with a few environment-file details worth checking before you copy it.

**cefjoeii/mern-crud** — A simple records system using MongoDB, Express.js, React.js, and Node.js with real-time CRUD operations using Socket.io

- Repository: https://github.com/cefjoeii/mern-crud
- Website: https://mern-crud-mpfr.onrender.com
- Stars: 764 · Forks: 516
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/cefjoeii-mern-crud

## One repository holds the API, the model and the React client

mern-crud is a records system built on MongoDB, Express.js, React.js and Node.js. The pieces are separated in the usual MERN way, and the top-level listing makes that visible: server.js is the entry point, routes/ holds the HTTP endpoints, models/ holds the database schemas, config/ holds runtime settings, and react-src/ holds the front end. public/ sits at the root because that is where the production build of the client lands.

Two implementation choices are stated up front. The back end exposes a REST API, and the front end uses Semantic UI React for its components. The distinguishing feature is the Socket.io layer, which broadcasts Create, Read, Update and Delete events in real time rather than leaving each connected browser to poll.

That design is what separates this from a plain CRUD tutorial. In a typical tutorial the browser only learns about a change after a refresh or a manual poll. Here the socket channel carries the change itself, so the record table on screen can be brought up to date by an event pushed from the server. The repository description calls the project a simple records system, and that simplicity is a fair description of the scope, but the socket wiring is the part that is worth studying.

## The back end runs on node server with a MongoDB URL in the environment

The documented back end setup is four steps. Fork and clone the repository, install dependencies, provide environment values, then start the server. The clone command is a fork-based one, which tells you the intended workflow:

```bash
git clone https://github.com/<your-user-name>/mern-crud.git
```

The environment file is where this project asks more of you than the code seems to need:

```env
DB=mongodb://localhost/mern-crud
PORT=3000
CORS=1
```

DB is the MongoDB connection string, PORT is where the Express app listens, and CORS switches cross-origin behaviour on for the separate React origin. The dependencies are installed with npm install at the repository root, and the server is started with node server. Once it is up, the documentation points you at http://localhost:3000 in a browser.

Here is the first snag worth flagging, and it is a documentation inconsistency rather than a design flaw. The README tells you to create a file named .env.local. The package.json scripts read a different file: start runs node --env-file='.env' server, while a separate script named start-no-env runs node server with no env file at all. If you rely on npm start after following the README literally, Node looks for .env and will not find .env.local. The Docker path sidesteps the question entirely, because docker-compose.yml sets DB, CORS and DEBUG as service environment variables rather than reading a file.

## The React client runs on port 4200 with its own API URL

The front end is a separate npm project that lives in react-src/. The documentation walks you into that directory, installs its dependencies with npm install, and gives it one environment value:

```env
REACT_APP_API_URL=http://localhost:3000
```

That variable is how the browser bundle learns where the API lives, and the REACT_APP_ prefix is what makes the value visible to a Create React App style build. The development server is then started with npm start and viewed at http://localhost:4200.

The split between the two ports explains the CORS setting in the back end. The API runs on 3000 and the client is served from 4200, so every request the client makes is cross-origin. The same arrangement reappears in docker-compose.yml, where the api service publishes 3000 and the web service publishes 4200 and passes REACT_APP_API_URL=http://localhost:3000.

For production, the documented path is npm run build inside react-src. The output is a folder named public created on the root directory, and the documentation is explicit that this is where the production-ready front end resides. The Dockerfile runs the same command, so the Node server and the built client end up served from one origin in the container image.

## docker-compose up gives you MongoDB, API and web in three containers

The container setup is the cleanest path through the project, and it is a single command:

```bash
docker-compose up
```

The compose file defines three services. db runs the mongo:7.0 image with no published port, so MongoDB is reachable inside the compose network only. api builds from the local Dockerfile, is named mern-crud, runs nodemon server, publishes 3000, and depends on db. web uses the same mern-crud image but runs npm start --prefix react-src, publishes 4200, and depends on api.

The dependency chain does the work that a human would otherwise do by hand. Because web waits for api and api waits for db, the client is not asked to talk to an API that cannot reach MongoDB.

The Dockerfile itself is short. It starts from node:latest, creates /usr/src/app/react-src, installs nodemon globally, copies package.json and runs npm install, does the same for react-src/package.json, copies the source over, exposes 3000 and 4200, and starts with CMD ["node", "server.js"]. Two details are worth noticing. The image installs nodemon even though the final CMD does not use it, because the compose file overrides the command with nodemon server for live reload during development. And the Dockerfile copies the whole tree, so anything you build locally into public/ ends up inside the image.

## The test script is a placeholder and the schema layer carries validation plugins

The README's to-do list marks Unit Tests as done, but package.json disagrees with it. The test script is:

```json
    "test": "echo \"Error: no test specified\" && exit 1"
```

That is the default stub npm generates for a new project, and it always exits with a failure. Whatever test coverage the checklist refers to is not wired to npm test, so a CI pipeline that runs npm test would go red on arrival. This is the clearest single reason to treat the repository as a reference implementation rather than a maintained package.

The back end dependencies tell you what the data layer does. mongoose handles connection and schemas, mongoose-validator and mongoose-unique-validator add field validation and uniqueness rules, body-parser reads request bodies, cors handles the cross-origin policy, and express-rate-limit provides throttling. string-capitalize-name suggests name fields are normalized on write, and socket.io is the broadcast layer.

The API routes live in routes/ and the schemas in models/, so a records system with more than one entity means adding a model file and a route file rather than touching a router table elsewhere. That layout is the part that scales up cleanly if you extend the project yourself.

## A 2017 release tag against 2026 commits tells you what this is

Maintenance facts here are thin and worth stating plainly. The repository is not archived, and the last push was on 2026-10-22, so work is still landing on the default branch, which is named develop. The only tagged release is v0.1.0, titled Initial Release, dated 2017-10-15. The homepage points at a deployed demo hosted on Render.

A nine-year gap between the only release and the most recent commit tells you how the project is consumed: as a repository to fork and adapt, not as a versioned dependency to pin. The dependency list is current, with express at 5.2.1 and mongoose at 9.9.4 in the caret ranges, which further suggests the branch head is the interesting artifact and the tag is historical.

For an alternative approach, compare this with the common pattern of a REST-only Express API plus a React client that polls or refetches on an interval. mern-crud keeps the same REST surface but adds the Socket.io channel, so a write on the server pushes to connected clients directly. The tradeoff is real in both directions: the socket path needs a persistent connection and reconnection handling on the client, and the client state has to merge server broadcasts with whatever the user is editing locally, which a simple refetch avoids entirely. This repository is a working example of the first approach, and the choice only makes sense if your app has more than one viewer of the same record.

## Conclusion

mern-crud fits a small team that wants one repository containing the whole stack, or a developer learning how REST endpoints and Socket.io broadcasts fit together. It does not fit a production service: the only tagged release is v0.1.0 from 2017-10-15, the test script exits with an error, and the documentation asks for a .env.local file while package.json reads .env. Before adopting it, run docker-compose up, confirm that the API on port 3000 reaches the MongoDB instance, and decide which env file name your deployment actually loads.

## FAQ

### How do I run mern-crud locally?

Install dependencies with npm install at the repository root, create an environment file with DB, PORT and CORS values, then run node server and open http://localhost:3000. The front end lives in react-src, where npm install, npm start and http://localhost:4200 complete the setup.

### Does mern-crud support Docker?

Yes. A Dockerfile and a docker-compose.yml are included, and docker-compose up starts three services: a mongo:7.0 database, an api service on port 3000 and a web service on port 4200 that depends on the api.

### Which environment file name does mern-crud expect?

The README asks you to create a .env.local file, but the package.json start script runs node --env-file='.env' server, so the script reads .env. A separate start-no-env script runs node server without loading any env file.

### Does mern-crud broadcast changes with Socket.io?

Yes. Socket.io is a dependency of the project and the to-do list marks real-time broadcast as done, on top of the REST API implemented on the back end. Semantic UI React is used for the front-end interface.

## Sources

- [cefjoeii/mern-crud on GitHub](https://github.com/cefjoeii/mern-crud)
- [License: MIT](https://github.com/cefjoeii/mern-crud/blob/develop/LICENSE)
- [Project website](https://mern-crud-mpfr.onrender.com)
- [README](https://github.com/cefjoeii/mern-crud/blob/develop/README.md)
- [Releases](https://github.com/cefjoeii/mern-crud/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cefjoeii-mern-crud
