Sitelet https://github.com/danitdev/MessageNode
Skip to content

Latest commit

Β 

History

111 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Message Node

A full-stack social messaging/feed application built while modernizing an older React project into a TypeScript + Express + Prisma backend.

The project is currently focused on building a clean REST API with proper service/controller separation, validation, file uploads, database access, and centralized error handling.


🚧 Project Status

In active development.

Current backend work includes:

  • TypeScript backend
  • Express server
  • Prisma ORM
  • MySQL database
  • Post model
  • User/Post relationship
  • Get all posts
  • Get a single post
  • Create a post
  • Update a post
  • Image upload with Multer
  • Static image serving
  • Image cleanup when replacing images
  • Custom AppError
  • Centralized Express error handling
  • Zod-based request validation setup
  • Delete post
  • Authentication/authorization
  • User ownership checks
  • Production-ready image storage
  • Complete API documentation
  • Frontend migration/modernization

πŸ› οΈ Tech Stack

Frontend

  • React
  • React Router
  • CSS
  • Fetch API

The frontend originated from an older React application and is being progressively adapted to communicate with the new backend.

Backend

  • Node.js
  • Express
  • TypeScript
  • Prisma
  • MySQL
  • Zod
  • Multer

Architecture

The backend follows a layered structure:

Request
   β”‚
   β–Ό
Express Route
   β”‚
   β–Ό
Controller
   β”‚
   β–Ό
Service
   β”‚
   β–Ό
Prisma
   β”‚
   β–Ό
MySQL

Controllers are responsible primarily for HTTP concerns, while services contain application/database logic.


πŸ“ Backend Architecture

The backend is organized by feature rather than putting all controllers and services into global folders.

Example:

src/
β”œβ”€β”€ errors/
β”‚   └── AppError.ts
β”‚
β”œβ”€β”€ lib/
β”‚   └── prisma.ts
β”‚
β”œβ”€β”€ utils/
β”‚   β”œβ”€β”€ path.ts
β”‚   └── deleteImage.ts
β”‚
β”œβ”€β”€ modules/
β”‚   └── posts/
β”‚       β”œβ”€β”€ postController.ts
β”‚       β”œβ”€β”€ postService.ts
β”‚       └── postSchema.ts
β”‚
β”œβ”€β”€ app.ts
└── server.ts

The exact folder names may evolve as the project continues to grow.


πŸ“ Posts

Posts currently contain information such as:

Post
β”œβ”€β”€ id
β”œβ”€β”€ title
β”œβ”€β”€ content
β”œβ”€β”€ imageUrl
β”œβ”€β”€ creatorId
└── createdAt

Posts have a relationship with a User through creatorId.


πŸ”Œ API

The current API is centered around posts.

Get all posts

GET /posts

Returns the posts ordered by creation date, along with creator information.

Example response:

{
  "posts": [
    {
      "id": 1,
      "title": "Hello World",
      "content": "My first post",
      "imageUrl": "/images/example.jpg",
      "creator": {
        "name": "User"
      }
    }
  ],
  "totalItems": 1
}

Get a single post

GET /posts/:postId

Returns a single post and its creator.

If the post doesn't exist:

404 Not Found

Create a post

POST /posts

The endpoint accepts multipart form data because posts can contain an image.

Example fields:

title
content
image

The image is processed using Multer and stored on the server.


Update a post

PUT /posts/:postId

The update endpoint currently accepts:

title
content
image

When a new image is uploaded:

New image
   ↓
Multer saves new file
   ↓
Database updated with new image URL
   ↓
Old image deleted

The old image is retrieved from the existing database record rather than trusting a client-provided path.


πŸ–ΌοΈ Image Uploads

Multer handles uploaded images.

Uploaded images are served through Express static middleware:

app.use("/images", express.static(...));

Images are referenced by URLs such as:

/images/1723456789-example.jpg

When replacing an existing post image, the backend:

  1. Finds the existing post.
  2. Stores the old image URL.
  3. Updates the database.
  4. Deletes the old image from disk.
  5. Returns the updated post.

The database update happens before deleting the old image to avoid deleting the existing image if the database update fails.


⚠️ Error Handling

The backend uses a custom AppError class for application-level errors.

Example:

throw new AppError("Couldn't find the post.", 404);

Controllers forward errors to Express:

catch (err) {
    next(err);
}

A centralized error-handling middleware is responsible for converting errors into HTTP responses.

This keeps controllers from having to duplicate error-response logic.


βœ… Validation

Request validation is being implemented using Zod.

Schemas define the expected structure of incoming data rather than relying solely on TypeScript types.

For example:

const createPostSchema = z.object({
    title: z.string(),
    content: z.string()
});

TypeScript types can then be inferred from schemas:

type CreatePostInput = z.infer<typeof createPostSchema>;

This provides both runtime validation and compile-time type safety.


πŸ—„οΈ Database

The project uses Prisma as its ORM with MySQL.

Prisma is responsible for:

  • Querying posts
  • Creating posts
  • Updating posts
  • Counting posts
  • Loading relationships
  • Selecting only required fields

For example, when checking whether a post exists, only the fields required by the operation can be selected:

const post = await prisma.post.findUnique({
    where: {
        id: postId
    },
    select: {
        id: true,
        imageUrl: true
    }
});

This avoids unnecessarily retrieving the entire record.


🧱 Service Layer

Database operations are kept inside services instead of directly inside controllers.

For example:

postController
      β”‚
      β–Ό
updatePostService()
      β”‚
      β”œβ”€β”€ validate operation
      β”œβ”€β”€ find existing post
      β”œβ”€β”€ update database
      └── clean up old image

This makes the controller primarily responsible for translating HTTP requests into service calls.


πŸ§ͺ Development

Install dependencies:

npm install

Generate the Prisma client:

npx prisma generate

Run the development server according to the project's configured npm scripts.


πŸ” Environment Variables

Sensitive configuration should be stored in environment variables rather than committed to Git.

Typical configuration includes:

DATABASE_URL="mysql://USER:PASSWORD@HOST:PORT/DATABASE"
PORT=8080

Do not commit .env files containing real credentials.


πŸ—ΊοΈ Roadmap

Backend

  • Complete CRUD operations
  • Authentication
  • Password hashing
  • JWT/session strategy
  • Authorization
  • Post ownership checks
  • Better request validation
  • Pagination
  • Filtering/search
  • Rate limiting
  • Production logging
  • Automated tests
  • API documentation

File Storage

The current implementation stores images locally.

Future production storage can use:

  • Amazon S3
  • Cloudflare R2
  • Cloudinary

This would allow the application to scale beyond a single server filesystem.

Frontend

  • Modernize React architecture
  • Connect all pages to the new API
  • Improve loading/error states
  • Modernize routing
  • Improve post editing
  • Authentication integration
  • Better API abstraction

🎯 Architecture Goals

The long-term goal is to turn Message Node into a maintainable full-stack application with:

  • Strong TypeScript typing
  • Runtime validation
  • Clean separation of concerns
  • Centralized error handling
  • Secure authentication and authorization
  • Scalable database access
  • Reliable file storage
  • Testable business logic
  • Production-ready API design

The project is intentionally being developed incrementally, with the backend architecture being improved rather than simply reproducing the original application's implementation.


πŸ“„ License

This project is currently a personal learning/development project.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages