Building a Scalable Pet Adoption Platform with Next.js, NestJS, PostgreSQL, and Tailwind CSS
Nadim Chowdhury
Posted on October 10, 2024
To develop the Pet Adoption Platform using Next.js, Tailwind CSS, NestJS, and PostgreSQL, I will outline the architecture, features, and required functionalities based on your requirements for front-end, back-end, and mobile app development.
Architecture Overview:
- Front-end: Developed using Next.js with Tailwind CSS for UI design.
- Back-end: Built using NestJS as the framework with PostgreSQL as the database.
- Mobile App: Implemented using React Native or a similar cross-platform framework, enabling pet browsing, application, and notifications for adoption updates.
Project Breakdown
Front-End (Next.js with Tailwind CSS)
-
Search for Adoptable Pets:
- Implement a search feature using Next.js' file-based routing.
-
UI Components:
- Search bar.
- Pet listing cards displaying details like name, breed, and location.
-
Functionalities:
- Fetch adoptable pets from the back-end.
- Filtering options (e.g., breed, age, location).
- Integration with back-end APIs to retrieve data.
-
Pet Filtering:
-
Filters:
- Location: Drop-down to filter by geographic area.
- Breed: Searchable list to filter by specific breeds.
- Age and size: Optional filters for narrowing results.
- Use Next.js dynamic routing to create detail pages for each pet.
-
Filters:
-
Responsive Design:
- Use Tailwind CSS for responsive layouts that adapt across desktop and mobile devices.
-
Adoption Form:
- Implement a form using React Hook Form or Formik for capturing user information and pet application details.
Back-End (NestJS with PostgreSQL)
-
User Registration and Authentication:
- Use NestJS modules for user authentication (JWT-based).
- Include registration, login, and password recovery features.
- Implement role-based access control (e.g., admin, user) for secure data management.
-
Pet Listings Management:
- CRUD operations for admins to add, edit, or remove pet listings.
-
Entities and Services in NestJS for pet-related data:
- Pet Entity: Stores information like breed, age, health status, etc.
- Pet Service: Manages business logic for pet listings.
- Integration with PostgreSQL using TypeORM.
-
Adoption Forms:
- Store and process user submissions in PostgreSQL.
- Implement status tracking (e.g., "application pending", "approved").
-
Notification System:
- Push notifications or emails to inform users about adoption status.
- Use Firebase Cloud Messaging (FCM) or Email Service for notifications.
-
API Structure:
- Modular architecture in NestJS with distinct modules for users, pets, and adoption forms.
- API routes for searching, listing pets, and handling adoption applications.
Mobile App (React Native with NestJS API)
-
Pet Browsing:
- Similar UI and functionality as the web front-end but adapted for mobile experience.
- Offline support: Cache data locally using a solution like AsyncStorage.
-
Push Notifications:
- Integrate Firebase Cloud Messaging (FCM) for mobile push notifications to alert users about new pets and application updates.
-
Adoption Applications:
- Allow users to submit adoption applications via the mobile app.
- Send notifications or email confirmations after form submissions.
Database Schema (PostgreSQL)
-
User Table:
- Fields:
id
,name
,email
,passwordHash
,role
. - Relations: Users to adoption applications (One-to-Many).
- Fields:
-
Pet Table:
- Fields:
id
,name
,breed
,age
,status
,location
. - Relations: Pets to applications (One-to-Many).
- Fields:
-
Adoption Application Table:
- Fields:
id
,userId
,petId
,status
,comments
.
- Fields:
Sample Folder Structure
Front-End (Next.js)
/pages
/adoptable-pets
[petId].tsx # Pet details page
/components
SearchBar.tsx
PetCard.tsx
/styles
globals.css # Tailwind CSS configuration
Back-End (NestJS)
/src
/modules
/users
users.controller.ts
users.service.ts
user.entity.ts
/pets
pets.controller.ts
pets.service.ts
pet.entity.ts
/adoption
adoption.controller.ts
adoption.service.ts
adoption.entity.ts
Mobile App (React Native)
/components
PetList.tsx
AdoptionForm.tsx
/screens
HomeScreen.tsx
PetDetailScreen.tsx
Key Functionalities
-
Front-End:
- Search adoptable pets by filters (location, breed).
- View detailed pet profiles.
- Apply for pet adoptions through forms.
-
Back-End:
- User authentication and management.
- Admin CRUD for pet listings.
- Submission and tracking of adoption forms.
-
Mobile App:
- Browse pets and apply for adoptions.
- Get notifications for updates.
You can start building the project by breaking down the tasks based on the modules above and iteratively integrating the functionality between the front-end, back-end, and mobile app. If you'd like detailed setup instructions or code snippets, feel free to ask!
For a Pet Adoption Platform using Next.js, Tailwind CSS, NestJS, and PostgreSQL, there are numerous features you can include depending on your goals, time, and resources. Below is a list of potential features, categorized into core and optional functionalities. You can pick and choose based on the scope of your project.
Core Features (Must-Have)
These are the essential features required for the MVP (Minimum Viable Product):
1. User Authentication and Profile Management
- User Registration and Login: Users can register and log in using email and password.
- Profile Management: Users can manage their profile, update details, and view adoption history.
2. Pet Listings and Search
- Browse Pets: Display a list of adoptable pets with images, breed, location, and other details.
- Search: Search for pets by name, breed, or other characteristics.
- Filter Pets: Filter pets by location, breed, age, size, or availability status.
3. Pet Details Page
- Detailed Pet Information: Each pet should have a detailed profile page with additional information like age, vaccination status, temperament, and adoption requirements.
- Image Gallery: Display multiple images of each pet.
4. Adoption Application Form
- Submit Adoption Requests: Allow users to submit an adoption request for a pet through a form.
- Application Status: Users can check the status of their applications (pending, approved, rejected).
5. Admin Dashboard
- CRUD for Pet Listings: Admins can add, edit, or delete pet listings.
- Manage Adoption Applications: Admins can view, approve, or reject adoption applications.
6. Responsive Design
- Mobile-Friendly UI: Ensure the website is fully responsive and works seamlessly on mobile devices.
Optional Features (Nice-to-Have)
These features can add extra value and improve the user experience:
1. User Notifications
- Email Notifications: Send users confirmation emails when they submit an adoption application or when the application status changes.
- Push Notifications: Notify users of new adoptable pets or changes in their application status (can be integrated using services like Firebase).
2. Favorite/Wishlist Pets
- Save Pets for Later: Allow users to save pets to a wishlist for easy access later.
3. Social Sharing
- Share Pet Profiles: Enable users to share pet profiles via social media platforms like Facebook, Twitter, or email.
4. Real-Time Chat or Messaging
- Chat with Admins or Shelters: Users can chat with admins or shelters to ask questions about the adoption process or a particular pet.
5. Donation Integration
- Donations: Allow users to make donations to the shelter or rescue organization.
6. Adoption Events and News
- Event Calendar: Display upcoming adoption events (in-person or virtual) hosted by the shelter.
- News Section: Share updates and news about the organization, success stories, etc.
7. Advanced Filtering
- More Detailed Filters: Add more advanced filtering options such as coat color, temperament, energy level, or special needs.
8. User Reviews and Testimonials
- Review Section: Let users who have adopted pets leave reviews or testimonials about their experience.
9. Referral Program
- Invite Friends: Users can invite their friends to the platform, earning rewards or discounts on future adoptions.
10. Location-Based Features
- Find Nearby Shelters: Users can search for pets based on their proximity to local shelters.
- Geolocation: Allow users to filter by pets within a certain radius of their current location.
Future Feature Ideas (Advanced)
These features can be developed once the core functionality is stable and your platform is growing:
1. Mobile App Integration
- React Native App: Develop a mobile app to enhance the user experience and accessibility.
- Push Notifications for Mobile: Enable real-time notifications for mobile users.
2. Shelter Portal
- Shelter Management System: Allow shelters to manage their own pet listings, view applications, and communicate with potential adopters.
3. AI/ML-Based Pet Recommendation
- Pet Matching Algorithm: Use AI/ML to recommend pets to users based on their preferences and previous interactions.
4. Success Stories and Blog
- Success Stories: Feature success stories from users who have successfully adopted pets through the platform.
- Blog: Add a blog section for pet care tips, adoption advice, and updates.
5. Payment Integration for Adoption Fees
- Payment Gateway: Integrate a payment gateway for users to pay adoption fees directly on the platform.
6. Pet Health Records
- Track Vaccination and Health: Store health and vaccination records for each pet that adopters can access.
Summary
For the initial version of the project, focusing on Core Features will provide a fully functional platform. Once those are stable, you can consider adding Optional Features and then gradually implement Future Feature Ideas based on user feedback and platform growth.
By prioritizing features based on user needs and development time, you can ensure your project remains manageable while still delivering value to your users.
Here’s a detailed frontend file and folder structure for the Pet Adoption Platform with all the core, optional, and future functionalities included. The structure is designed for Next.js, Tailwind CSS, and best practices in modularity, scalability, and maintainability.
Full Folder Structure
├── public/ # Publicly accessible files (images, icons, etc.)
│ ├── images/ # Store static images for pets, logos, etc.
│ └── favicon.ico # Site favicon
├── src/ # Project source folder
│ ├── components/ # Reusable components
│ │ ├── Adoption/ # Adoption-related components
│ │ │ ├── AdoptionForm.tsx
│ │ │ ├── ApplicationStatus.tsx
│ │ │ ├── ApplicationList.tsx
│ │ └── Pet/ # Pet-related components
│ │ ├── PetCard.tsx # Pet card for list display
│ │ ├── PetDetails.tsx # Detailed view of a pet
│ │ ├── PetFilters.tsx # Filtering panel for searching
│ │ └── PetList.tsx # List view of all pets
│ ├── hooks/ # Custom hooks
│ │ └── useFetch.ts # Fetch data hook
│ ├── layout/ # Shared layout components
│ │ ├── Header.tsx # Header (Navbar)
│ │ ├── Footer.tsx # Footer component
│ │ └── Sidebar.tsx # Admin/Sidebar menu
│ ├── pages/ # Next.js pages (routing)
│ │ ├── api/ # API routes for server-side handling
│ │ │ ├── pets/ # API for pet operations
│ │ │ ├── users/ # API for user-related actions
│ │ ├── adoptable-pets/ # Dynamic routes for pets
│ │ │ └── [id].tsx # Individual pet detail page
│ │ ├── account/ # User account management
│ │ │ ├── profile.tsx # User profile management page
│ │ │ └── applications.tsx # View user's submitted adoption applications
│ │ ├── admin/ # Admin dashboard
│ │ │ ├── index.tsx # Admin dashboard main page
│ │ │ ├── pets.tsx # CRUD operations for pets
│ │ │ ├── applications.tsx # Manage adoption applications
│ │ └── index.tsx # Home page
│ │ └── adopt.tsx # Adoption page (form submission)
│ │ └── favorites.tsx # User's favorite pets (wishlist)
│ │ └── about.tsx # Static About Us page
│ ├── services/ # Services for API calls
│ │ ├── petService.ts # API calls for fetching pet data
│ │ ├── userService.ts # User authentication and profile management services
│ │ └── applicationService.ts # Services for handling adoption applications
│ ├── styles/ # Global styles
│ │ ├── globals.css # Global Tailwind CSS styles
│ │ └── tailwind.css # Custom Tailwind styles (if needed)
│ ├── utils/ # Utility functions
│ │ ├── helpers.ts # General helper functions (e.g., formatting)
│ │ └── validators.ts # Form validation helpers
│ └── config/ # Configuration settings
│ └── api.ts # API base URL and config settings
├── .env.local # Environment variables for sensitive data
├── next.config.js # Next.js configuration
├── tailwind.config.js # Tailwind CSS configuration
├── postcss.config.js # PostCSS configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Project dependencies and scripts
└── README.md # Project documentation
Detailed Breakdown of the Folder Structure
1. Public Folder
-
/public/images/
: Contains static assets like images, icons, or logos that will be used in the platform, including pet images and user profile pictures. -
favicon.ico
: Favicon for the platform.
2. Components Folder
-
/components/Adoption/
: Contains all the components related to the adoption process.-
AdoptionForm.tsx
: Form for users to submit adoption requests. -
ApplicationStatus.tsx
: Displays the status of a user's adoption application. -
ApplicationList.tsx
: Lists all the applications submitted by a user or managed by an admin.
-
-
/components/Pet/
: Components related to the pet listings and details.-
PetCard.tsx
: Displays the pet's basic information in a card format (e.g., name, breed, and image). -
PetDetails.tsx
: Displays detailed information about a pet on a dedicated page. -
PetFilters.tsx
: Provides filter options for users to search and filter pets by breed, age, and location. -
PetList.tsx
: Renders a list of pets based on search or filter results.
-
3. Hooks Folder
-
useFetch.ts
: Custom hook to handle data fetching usingfetch()
oraxios
. This hook will handle API calls to the backend for fetching pets, adoption applications, and user data.
4. Layout Folder
-
Header.tsx
: Contains the navigation bar, allowing users to browse through the platform. -
Footer.tsx
: Footer with platform-wide links like About Us, Privacy Policy, etc. -
Sidebar.tsx
: Admin panel sidebar for navigating between different admin functionalities (e.g., managing pets, reviewing applications).
5. Pages Folder
-
/pages/api/
: Custom server-side API routes for pets, users, and adoption-related logic. These can be used if you plan to manage some backend logic within the Next.js app. -
/pages/adoptable-pets/[id].tsx
: Dynamic route for individual pet details based on the pet ID. -
/pages/account/
: Handles user account pages:-
profile.tsx
: User profile page where they can update their information. -
applications.tsx
: Displays the applications the user has submitted.
-
-
/pages/admin/
: Admin pages for managing the platform:-
index.tsx
: Admin dashboard overview. -
pets.tsx
: Admin interface to create, edit, and delete pets. -
applications.tsx
: Manage and approve/reject adoption applications.
-
-
/pages/favorites.tsx
: User's wishlist or favorite pets. -
/pages/about.tsx
: Static About Us page.
6. Services Folder
-
petService.ts
: Handles all the API calls related to pet data (e.g., fetching pets, filtering, and sorting). -
userService.ts
: Handles user-related API calls, such as authentication, profile updates, etc. -
applicationService.ts
: Manages API calls for submitting and reviewing adoption applications.
7. Styles Folder
-
globals.css
: Global CSS, including base Tailwind CSS imports and any custom global styles. -
tailwind.css
: (Optional) Custom Tailwind configuration or specific utility classes.
8. Utils Folder
-
helpers.ts
: General helper functions, like formatting dates or handling complex data structures. -
validators.ts
: Validation functions for form input, e.g., email, name validation for the adoption form.
9. Config Folder
-
api.ts
: Stores the base URL for API calls and any other API-related configurations.
Core Features Implementation
1. Pet Search and Filter
- The
PetFilters.tsx
component handles user input for filtering pets by breed, location, and age. It triggers the API call throughuseFetch
to update the displayed list of pets.
2. Pet List and Details
-
PetList.tsx
: Fetches and displays pets based on the search query or filters. -
PetCard.tsx
: Displays each pet in a card format with the essential information and a link to view more details. -
PetDetails.tsx
: Provides a detailed view of each pet, including a button to apply for adoption.
3. Adoption Application
-
AdoptionForm.tsx
: A form where users can apply for a specific pet's adoption. The form will validate user inputs and submit the application via theapplicationService.ts
.
4.
User Profile and Application Tracking
-
Profile.tsx
: Allows users to update their profile and view their adoption history. -
ApplicationList.tsx
: Displays a user's submitted adoption applications and their current status.
5. Favorites/Wishlist
-
favorites.tsx
: Displays pets the user has favorited or saved for later.
Optional and Future Features Implementation
-
Notifications: Add a
Notification.tsx
component to display messages like adoption application confirmation or updates. - Referral Program: Implement a referral program by adding the referral system in user profiles and generating unique referral links.
- Geolocation for Nearby Shelters: Use a third-party API to integrate a location-based search for nearby pets.
This file structure and breakdown will help you implement a robust, feature-rich Pet Adoption Platform that is modular, scalable, and easy to maintain.
Here is the backend file and folder structure for the Pet Adoption Platform using NestJS and PostgreSQL. This structure is designed for scalability and modularity, following best practices for building maintainable backend services.
Backend Folder Structure
├── src/
│ ├── common/ # Shared resources like guards, filters, DTOs, utilities
│ │ ├── dto/ # Shared DTOs
│ │ ├── filters/ # Exception filters
│ │ ├── guards/ # Authorization guards (e.g., roles)
│ │ └── utils/ # Utility functions used across modules
│ ├── config/ # Configuration files (e.g., environment variables)
│ │ ├── config.module.ts # Config service for environment variables
│ │ ├── config.service.ts # Configuration management logic
│ └── database/ # Database-related files (TypeORM, migrations, etc.)
│ ├── entities/ # Database entities for TypeORM
│ │ ├── pet.entity.ts # Pet entity
│ │ ├── user.entity.ts # User entity
│ │ ├── application.entity.ts # Adoption application entity
│ ├── migrations/ # TypeORM migration files
│ └── database.module.ts # Database connection configuration
│ ├── modules/ # Core feature modules
│ │ ├── auth/ # Authentication module (JWT)
│ │ │ ├── auth.controller.ts # Auth-related routes (login, register)
│ │ │ ├── auth.module.ts # Auth module declaration
│ │ │ ├── auth.service.ts # Authentication logic
│ │ │ ├── jwt.strategy.ts # JWT Strategy for securing routes
│ │ │ ├── local.strategy.ts # Local strategy (login with email/password)
│ │ │ └── dto/ # DTOs for login, registration, etc.
│ │ ├── users/ # User management module
│ │ │ ├── users.controller.ts # User-related routes
│ │ │ ├── users.module.ts # User module
│ │ │ ├── users.service.ts # User management logic (CRUD, profile updates)
│ │ │ ├── user.entity.ts # User entity (shared with database/entities)
│ │ │ └── dto/ # User-specific DTOs (Data Transfer Objects)
│ │ ├── pets/ # Pets management module
│ │ │ ├── pets.controller.ts # Routes for handling pet listings
│ │ │ ├── pets.module.ts # Pet module declaration
│ │ │ ├── pets.service.ts # Business logic for pets
│ │ │ ├── pet.entity.ts # Pet entity (shared with database/entities)
│ │ │ └── dto/ # Pet-related DTOs (for validation)
│ │ ├── adoption/ # Adoption management module
│ │ │ ├── adoption.controller.ts # Routes for submitting and managing applications
│ │ │ ├── adoption.module.ts # Adoption module declaration
│ │ │ ├── adoption.service.ts # Business logic for adoption processes
│ │ │ ├── application.entity.ts # Application entity (shared with database/entities)
│ │ │ └── dto/ # Adoption-related DTOs
│ │ ├── notifications/ # Notification handling (email, SMS, push notifications)
│ │ │ ├── notifications.module.ts # Notifications module declaration
│ │ │ ├── notifications.service.ts # Notification logic (for emails, push notifications)
│ │ │ ├── email.service.ts # Email sending service
│ │ │ └── push.service.ts # Push notification logic (Firebase integration)
│ │ ├── admin/ # Admin-specific functionality
│ │ │ ├── admin.controller.ts # Routes for admin operations (approving adoptions, managing pets)
│ │ │ ├── admin.module.ts # Admin module declaration
│ │ │ └── admin.service.ts # Admin-specific business logic
│ ├── app.module.ts # Root application module
│ ├── main.ts # Entry point of the application
├── test/ # Unit and integration tests
│ ├── pets/ # Test files for pet module
│ │ └── pets.service.spec.ts
│ └── users/ # Test files for user module
│ └── users.service.spec.ts
├── .env # Environment configuration
├── nest-cli.json # NestJS CLI configuration
├── tsconfig.json # TypeScript configuration
└── package.json # Node.js dependencies and scripts
Detailed Breakdown of Key Folders and Files:
Common Folder (/src/common
)
-
DTOs (
/src/common/dto/
): Shared Data Transfer Objects for validation across multiple modules. -
Guards (
/src/common/guards/
): Custom guards for protecting routes, such as JWT authentication or role-based access. -
Filters (
/src/common/filters/
): Exception filters for catching and transforming exceptions across the app. -
Utils (
/src/common/utils/
): Utility functions that are used across multiple modules, such as helpers for date formatting or pagination.
Config Folder (/src/config
)
-
config.service.ts
: Centralized configuration logic for handling environment variables using@nestjs/config
. This ensures easy access to environment variables for different services like database connection strings or API keys. -
config.module.ts
: NestJS module to manage the application’s configuration.
Database Folder (/src/database
)
-
Entities (
/src/database/entities/
): Stores TypeORM entities for modeling data structures such as User, Pet, and AdoptionApplication.-
user.entity.ts
: Defines theUser
entity with fields likeid
,name
,email
, and their relationships to pets or applications. -
pet.entity.ts
: Defines thePet
entity with attributes such asname
,breed
,age
,location
. -
application.entity.ts
: Defines theAdoptionApplication
entity, linking users and pets with fields such asstatus
,comments
, and timestamps.
-
-
Migrations (
/src/database/migrations/
): Store migration files for database schema updates using TypeORM.
Modules Folder (/src/modules
)
-
Auth Module (
/src/modules/auth/
): Manages authentication using JWT and login strategies.-
auth.controller.ts
: Handles routes for login, registration, and token refresh. -
auth.service.ts
: Implements the business logic for user authentication and JWT generation. -
jwt.strategy.ts
: Strategy for validating JWT tokens.
-
-
Users Module (
/src/modules/users/
): Manages user registration, profile management, and user CRUD.-
users.controller.ts
: Exposes API routes for user management. -
users.service.ts
: Contains business logic related to user operations (e.g., update profile, get user data).
-
-
Pets Module (
/src/modules/pets/
): Manages pet listing CRUD (create, read, update, delete).-
pets.controller.ts
: Provides routes for managing pet listings (e.g.,/pets
,/pets/:id
). -
pets.service.ts
: Handles business logic for pet operations (e.g., adding, deleting, updating pets).
-
-
Adoption Module (
/src/modules/adoption/
): Handles adoption requests and application management.-
adoption.controller.ts
: Routes for managing adoption applications (e.g., submit, update status). -
adoption.service.ts
: Handles application processing logic.
-
-
Notifications Module (
/src/modules/notifications/
): Handles email notifications, push notifications, and SMS alerts.-
email.service.ts
: Contains logic for sending emails (e.g., using a service like SendGrid). -
push.service.ts
: Manages push notifications (e.g., via Firebase).
-
Admin Module (
/src/modules/admin/
): Manages admin-specific functionalities, such as reviewing adoption applications and managing users or pets.
Root Files
-
app.module.ts
: The root module of the NestJS app that imports other feature modules. -
main.ts
: The entry point of the NestJS app, where the app is initialized and run.
Tests Folder (/test/
)
- Contains unit tests and integration tests for the core modules (e.g., Pets, Users).
-
pets.service.spec.ts
: Unit tests for the pet service. users.service.spec.ts
-
: Unit tests for the user service.
Example of Key Files
auth.service.ts
Handles user authentication and JWT token creation:
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import { LoginDto, RegisterDto } from './dto';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async login(loginDto: LoginDto) {
const user = await this.usersService.validateUser(
loginDto.email,
loginDto.password,
);
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
async register(registerDto: RegisterDto) {
const newUser = await this.usersService.create(registerDto);
return this.login({ email: newUser.email, password: registerDto.password });
}
}
pets.controller.ts
Provides routes for fetching and managing pets:
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { PetsService } from './pets.service';
import { CreatePetDto, UpdatePetDto } from './dto';
@Controller('pets')
export class PetsController {
constructor(private readonly petsService: PetsService) {}
@Get()
findAll() {
return this.petsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.petsService.findOne(id);
}
@Post()
create(@Body() createPetDto: CreatePetDto) {
return this.petsService.create(createPetDto);
}
@Put(':id')
update(@Param('id') id: string, @Body() updatePetDto: UpdatePetDto) {
return this.petsService.update(id, updatePetDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.petsService.remove(id);
}
}
pet.entity.ts
Defines the Pet entity with TypeORM:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Pet {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
breed: string;
@Column()
age: number;
@Column()
location: string;
@Column({ default: true })
isAvailable: boolean;
}
Conclusion
This modular folder structure is designed to handle core features like user authentication, pet management, adoption processes, and notifications. It provides a scalable framework to add more features in the future, while keeping the codebase clean and maintainable.
Here’s the complete code for the Adoption-related components and Pet-related components based on the structure you provided:
Folder Structure:
├── public/
│ ├── images/
│ └── favicon.ico
├── src/
│ ├── components/
│ │ ├── Adoption/
│ │ │ ├── AdoptionForm.tsx
│ │ │ ├── ApplicationStatus.tsx
│ │ │ ├── ApplicationList.tsx
│ │ └── Pet/
│ │ ├── PetCard.tsx
│ │ ├── PetDetails.tsx
│ │ ├── PetFilters.tsx
│ │ └── PetList.tsx
1. Adoption Components
1.1 AdoptionForm.tsx
This component handles the adoption application form.
import { useState } from 'react';
const AdoptionForm = ({ petId }: { petId: string }) => {
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
});
const [submitted, setSubmitted] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Send data to the server
await fetch('/api/adoptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ ...formData, petId }),
});
setSubmitted(true);
};
return (
<div className="max-w-lg mx-auto">
<h2 className="text-2xl font-bold">Adopt {petId}</h2>
{submitted ? (
<p className="text-green-500">Your application has been submitted!</p>
) : (
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium">Name</label>
<input
type="text"
id="name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium">Email</label>
<input
type="email"
id="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<div className="mb-4">
<label htmlFor="message" className="block text-sm font-medium">Why do you want to adopt this pet?</label>
<textarea
id="message"
value={formData.message}
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
className="mt-1 p-2 w-full border rounded-md"
required
/>
</div>
<button type="submit" className="bg-blue-500 text-white p-2 rounded-md">
Submit Application
</button>
</form>
)}
</div>
);
};
export default AdoptionForm;
1.2 ApplicationStatus.tsx
Displays the status of the adoption application.
const ApplicationStatus = ({ status }: { status: string }) => {
return (
<div className="border p-4 rounded-lg bg-gray-50">
<h3 className="font-bold text-xl mb-2">Application Status</h3>
<p className={`text-${status === 'Approved' ? 'green' : 'red'}-500`}>
{status === 'Approved' ? 'Your application has been approved!' : 'Your application is still under review.'}
</p>
</div>
);
};
export default ApplicationStatus;
1.3 ApplicationList.tsx
Lists all the adoption applications for a user.
import { useEffect, useState } from 'react';
interface Application {
id: string;
petName: string;
status: string;
}
const ApplicationList = () => {
const [applications, setApplications] = useState<Application[]>([]);
useEffect(() => {
// Fetch user applications from the backend
fetch('/api/adoptions')
.then((response) => response.json())
.then((data) => setApplications(data));
}, []);
return (
<div className="max-w-3xl mx-auto">
<h2 className="text-2xl font-bold mb-4">Your Adoption Applications</h2>
{applications.length === 0 ? (
<p>No applications found.</p>
) : (
<ul>
{applications.map((application) => (
<li key={application.id} className="mb-4 border p-4 rounded-lg">
<h3 className="font-bold">{application.petName}</h3>
<p>Status: {application.status}</p>
</li>
))}
</ul>
)}
</div>
);
};
export default ApplicationList;
2. Pet Components
2.1 PetCard.tsx
Displays a pet card with basic information.
import Link from 'next/link';
interface PetCardProps {
pet: {
id: string;
name: string;
breed: string;
age: number;
imageUrl: string;
location: string;
};
}
const PetCard = ({ pet }: PetCardProps) => {
return (
<div className="border rounded-lg overflow-hidden shadow-lg">
<img src={pet.imageUrl} alt={pet.name} className="w-full h-48 object-cover" />
<div className="p-4">
<h3 className="text-xl font-bold">{pet.name}</h3>
<p>Breed: {pet.breed}</p>
<p>Age: {pet.age} years</p>
<p>Location: {pet.location}</p>
<Link href={`/adoptable-pets/${pet.id}`}>
<a className="text-blue-500 mt-2 inline-block">View Details</a>
</Link>
</div>
</div>
);
};
export default PetCard;
2.2 PetDetails.tsx
Displays detailed information about a specific pet.
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import AdoptionForm from '../Adoption/AdoptionForm';
interface PetDetailsProps {
id: string;
name: string;
breed: string;
age: number;
description: string;
imageUrl: string;
location: string;
}
const PetDetails = () => {
const router = useRouter();
const { id } = router.query;
const [pet, setPet] = useState<PetDetailsProps | null>(null);
useEffect(() => {
if (id) {
// Fetch pet details from the backend
fetch(`/api/pets/${id}`)
.then((response) => response.json())
.then((data) => setPet(data));
}
}, [id]);
if (!pet) return <p>Loading...</p>;
return (
<div className="max-w-4xl mx-auto mt-8">
<img src={pet.imageUrl} alt={pet.name} className="w-full h-96 object-cover rounded-md" />
<div className="mt-4">
<h2 className="text-3xl font-bold">{pet.name}</h2>
<p className="text-lg">Breed: {pet.breed}</p>
<p className="text-lg">Age: {pet.age} years</p>
<p className="mt-4">{pet.description}</p>
<p className="mt-4">Location: {pet.location}</p>
<AdoptionForm petId={pet.id} />
</div>
</div>
);
};
export default PetDetails;
2.3 PetFilters.tsx
A filter panel to filter pets by breed, age, and location.
import { useState } from 'react';
const PetFilters = ({ onFilter }: { onFilter: (filters: any) => void }) => {
const [filters, setFilters] = useState({
breed: '',
location: '',
age: '',
});
const handleFilterChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
setFilters({
...filters,
[e.target.name]: e.target.value,
});
};
const handleApplyFilters = () => {
onFilter(filters);
};
return (
<div className="border p-4 rounded-lg bg-gray-50">
<h3 className="font-bold mb-4">Filter Pets</h3>
<div className="mb-4">
<label htmlFor="breed"
className="block text-sm font-medium">Breed</label>
<input
type="text"
id="breed"
name="breed"
value={filters.breed}
onChange={handleFilterChange}
className="w-full border p-2 rounded-md"
/>
</div>
<div className="mb-4">
<label htmlFor="location" className="block text-sm font-medium">Location</label>
<input
type="text"
id="location"
name="location"
value={filters.location}
onChange={handleFilterChange}
className="w-full border p-2 rounded-md"
/>
</div>
<div className="mb-4">
<label htmlFor="age" className="block text-sm font-medium">Age</label>
<input
type="number"
id="age"
name="age"
value={filters.age}
onChange={handleFilterChange}
className="w-full border p-2 rounded-md"
/>
</div>
<button
onClick={handleApplyFilters}
className="bg-blue-500 text-white p-2 rounded-md w-full"
>
Apply Filters
</button>
</div>
);
};
export default PetFilters;
2.4 PetList.tsx
Displays a list of pets filtered by search or filter options.
import { useEffect, useState } from 'react';
import PetCard from './PetCard';
const PetList = () => {
const [pets, setPets] = useState([]);
const [filters, setFilters] = useState({});
useEffect(() => {
// Fetch the list of pets from the backend
fetch('/api/pets')
.then((response) => response.json())
.then((data) => setPets(data));
}, []);
const handleFilter = (filterValues: any) => {
setFilters(filterValues);
// You can also re-fetch pets based on filters using an API call
};
return (
<div>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
{pets.map((pet) => (
<PetCard key={pet.id} pet={pet} />
))}
</div>
</div>
);
};
export default PetList;
Conclusion:
This complete code for the AdoptionForm, ApplicationStatus, PetCard, PetDetails, PetFilters, and PetList components will handle the key functionalities like listing pets, viewing detailed pet information, filtering pets, and submitting adoption applications. You can integrate these with backend services to create a fully functional pet adoption platform.
Here’s the complete code for the custom hooks, layout components, and API routes based on your structure.
Folder Structure:
├── src/
│ ├── hooks/ # Custom hooks
│ │ └── useFetch.ts # Fetch data hook
│ ├── layout/ # Shared layout components
│ │ ├── Header.tsx # Header (Navbar)
│ │ ├── Footer.tsx # Footer component
│ │ └── Sidebar.tsx # Admin/Sidebar menu
│ ├── pages/ # Next.js pages (routing)
│ │ ├── api/ # API routes for server-side handling
│ │ │ ├── pets/ # API for pet operations
│ │ │ ├── users/ # API for user-related actions
1. Custom Hooks
1.1 useFetch.ts
A simple reusable custom hook to handle data fetching.
import { useEffect, useState } from 'react';
const useFetch = (url: string) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (error: any) {
setError(error.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
export default useFetch;
2. Layout Components
2.1 Header.tsx
A responsive header (navbar) component with links to different parts of the site.
import Link from 'next/link';
const Header = () => {
return (
<header className="bg-blue-500 p-4 text-white shadow-md">
<div className="container mx-auto flex justify-between items-center">
<h1 className="text-2xl font-bold">
<Link href="/">Pet Adoption Platform</Link>
</h1>
<nav>
<ul className="flex space-x-4">
<li><Link href="/">Home</Link></li>
<li><Link href="/adoptable-pets">Pets</Link></li>
<li><Link href="/about">About Us</Link></li>
<li><Link href="/account">Account</Link></li>
</ul>
</nav>
</div>
</header>
);
};
export default Header;
2.2 Footer.tsx
A simple footer component with some additional links.
const Footer = () => {
return (
<footer className="bg-gray-800 text-white p-4">
<div className="container mx-auto text-center">
<p>© {new Date().getFullYear()} Pet Adoption Platform</p>
<ul className="flex justify-center space-x-4 mt-2">
<li><a href="/privacy">Privacy Policy</a></li>
<li><a href="/terms">Terms of Service</a></li>
<li><a href="/contact">Contact Us</a></li>
</ul>
</div>
</footer>
);
};
export default Footer;
2.3 Sidebar.tsx
Admin sidebar menu for navigation within the admin dashboard.
import Link from 'next/link';
const Sidebar = () => {
return (
<aside className="bg-gray-100 w-64 p-4 shadow-md">
<nav>
<ul>
<li className="mb-4"><Link href="/admin">Dashboard</Link></li>
<li className="mb-4"><Link href="/admin/pets">Manage Pets</Link></li>
<li className="mb-4"><Link href="/admin/applications">Adoption Applications</Link></li>
<li className="mb-4"><Link href="/admin/users">Manage Users</Link></li>
</ul>
</nav>
</aside>
);
};
export default Sidebar;
3. API Routes
3.1 /pages/api/pets/index.ts
API route to handle pet listing operations such as retrieving all pets or adding a new pet.
import { NextApiRequest, NextApiResponse } from 'next';
// Mock data for demo purposes
const pets = [
{ id: 1, name: 'Max', breed: 'Labrador', age: 3, location: 'New York' },
{ id: 2, name: 'Bella', breed: 'Poodle', age: 2, location: 'Los Angeles' },
{ id: 3, name: 'Charlie', breed: 'Beagle', age: 4, location: 'Chicago' },
];
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'GET') {
// Retrieve list of all pets
res.status(200).json(pets);
} else if (req.method === 'POST') {
// Add a new pet (for simplicity, we don't save this anywhere)
const newPet = req.body;
pets.push(newPet);
res.status(201).json(newPet);
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
3.2 /pages/api/pets/[id].ts
API route to handle operations for a specific pet (e.g., retrieving details, updating, deleting).
import { NextApiRequest, NextApiResponse } from 'next';
// Mock data for demo purposes
const pets = [
{ id: 1, name: 'Max', breed: 'Labrador', age: 3, location: 'New York' },
{ id: 2, name: 'Bella', breed: 'Poodle', age: 2, location: 'Los Angeles' },
{ id: 3, name: 'Charlie', breed: 'Beagle', age: 4, location: 'Chicago' },
];
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { id } = req.query;
const pet = pets.find((p) => p.id === Number(id));
if (!pet) {
res.status(404).json({ message: 'Pet not found' });
return;
}
if (req.method === 'GET') {
// Return specific pet details
res.status(200).json(pet);
} else if (req.method === 'PUT') {
// Update pet details
const updatedPet = { ...pet, ...req.body };
pets[pets.findIndex((p) => p.id === Number(id))] = updatedPet;
res.status(200).json(updatedPet);
} else if (req.method === 'DELETE') {
// Delete pet
const index = pets.findIndex((p) => p.id === Number(id));
pets.splice(index, 1);
res.status(204).end();
} else {
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
3.3 /pages/api/users/index.ts
API route to handle user operations such as retrieving user information or registering a new user.
import { NextApiRequest, NextApiResponse } from 'next';
// Mock user data
const users = [
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'user' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'admin' },
];
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'GET') {
// Retrieve all users
res.status(200).json(users);
} else if (req.method === 'POST') {
// Register a new user (for simplicity, we don't persist the user)
const newUser = req.body;
users.push(newUser);
res.status(201).json(newUser);
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
Summary:
-
Custom Hook:
useFetch.ts
handles data fetching for any API. -
Layout Components:
-
Header.tsx
provides navigation across the site. -
Footer.tsx
provides a footer with essential links. -
Sidebar.tsx
is a simple admin sidebar menu.
-
-
API Routes:
-
/api/pets/index.ts
provides API functionality for listing pets and adding new ones. -
/api/pets/[id].ts
handles specific pet operations like retrieving, updating, and deleting pets. -
/api/users/index.ts
manages user operations such as retrieving or adding new
-
users.
These components and APIs together form the base structure for managing pets, users, and general UI elements for the platform.
Here is the full code for the dynamic pet detail page, user account management, and admin dashboard based on the structure you provided.
Folder Structure:
├── src/
│ ├── pages/
│ │ ├── adoptable-pets/ # Dynamic routes for pets
│ │ │ └── [id].tsx # Individual pet detail page
│ │ ├── account/ # User account management
│ │ │ ├── profile.tsx # User profile management page
│ │ │ └── applications.tsx # View user's submitted adoption applications
│ │ ├── admin/ # Admin dashboard
│ │ │ ├── index.tsx # Admin dashboard main page
│ │ │ ├── pets.tsx # CRUD operations for pets
│ │ │ ├── applications.tsx # Manage adoption applications
1. Dynamic Pet Detail Page
/adoptable-pets/[id].tsx
This page dynamically displays details of an individual pet based on its ID.
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import AdoptionForm from '../../components/Adoption/AdoptionForm';
const PetDetails = () => {
const router = useRouter();
const { id } = router.query; // Dynamic route parameter
const [pet, setPet] = useState<any>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (id) {
// Fetch the pet details by ID
fetch(`/api/pets/${id}`)
.then((res) => res.json())
.then((data) => {
setPet(data);
setLoading(false);
});
}
}, [id]);
if (loading) return <p>Loading...</p>;
if (!pet) return <p>Pet not found</p>;
return (
<div className="container mx-auto p-8">
<div className="flex flex-col items-center">
<img src={pet.imageUrl} alt={pet.name} className="w-1/2 rounded-md shadow-lg" />
<h2 className="text-3xl font-bold mt-4">{pet.name}</h2>
<p className="text-xl">Breed: {pet.breed}</p>
<p className="text-xl">Age: {pet.age} years</p>
<p className="text-xl">Location: {pet.location}</p>
<p className="text-md mt-4">{pet.description}</p>
{/* Adoption Form */}
<div className="mt-6 w-full md:w-1/2">
<AdoptionForm petId={id as string} />
</div>
</div>
</div>
);
};
export default PetDetails;
2. User Account Management
2.1 /account/profile.tsx
A page where users can manage their account information.
import { useState, useEffect } from 'react';
const Profile = () => {
const [profileData, setProfileData] = useState({
name: '',
email: '',
});
const [loading, setLoading] = useState(true);
const [message, setMessage] = useState('');
useEffect(() => {
// Fetch user profile data from the backend
fetch('/api/users/me')
.then((res) => res.json())
.then((data) => {
setProfileData(data);
setLoading(false);
});
}, []);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setProfileData({
...profileData,
[e.target.name]: e.target.value,
});
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Update user profile data
const response = await fetch('/api/users/me', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(profileData),
});
const result = await response.json();
setMessage(result.message || 'Profile updated successfully.');
};
if (loading) return <p>Loading profile...</p>;
return (
<div className="container mx-auto p-8">
<h2 className="text-2xl font-bold mb-4">Your Profile</h2>
{message && <p className="text-green-500 mb-4">{message}</p>}
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label htmlFor="name" className="block text-sm font-medium">Name</label>
<input
type="text"
id="name"
name="name"
value={profileData.name}
onChange={handleChange}
className="w-full p-2 border rounded-md"
/>
</div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium">Email</label>
<input
type="email"
id="email"
name="email"
value={profileData.email}
onChange={handleChange}
className="w-full p-2 border rounded-md"
/>
</div>
<button
type="submit"
className="bg-blue-500 text-white p-2 rounded-md"
>
Save Changes
</button>
</form>
</div>
);
};
export default Profile;
2.2 /account/applications.tsx
A page where users can view their submitted adoption applications.
import { useEffect, useState } from 'react';
interface Application {
id: string;
petName: string;
status: string;
}
const Applications = () => {
const [applications, setApplications] = useState<Application[]>([]);
useEffect(() => {
// Fetch the user's adoption applications
fetch('/api/adoptions/me')
.then((res) => res.json())
.then((data) => setApplications(data));
}, []);
return (
<div className="container mx-auto p-8">
<h2 className="text-2xl font-bold mb-4">Your Adoption Applications</h2>
{applications.length === 0 ? (
<p>No applications found.</p>
) : (
<ul>
{applications.map((application) => (
<li key={application.id} className="mb-4 p-4 border rounded-lg">
<h3 className="font-bold">Pet: {application.petName}</h3>
<p>Status: {application.status}</p>
</li>
))}
</ul>
)}
</div>
);
};
export default Applications;
3. Admin Dashboard
3.1 /admin/index.tsx
Admin dashboard main page, providing an overview of the platform.
import Link from 'next/link';
const AdminDashboard = () => {
return (
<div className="container mx-auto p-8">
<h2 className="text-3xl font-bold mb-6">Admin Dashboard</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
<Link href="/admin/pets">
<a className="bg-blue-500 text-white p-4 rounded-md shadow-lg text-center">
Manage Pets
</a>
</Link>
<Link href="/admin/applications">
<a className="bg-blue-500 text-white p-4 rounded-md shadow-lg text-center">
Manage Applications
</a>
</Link>
<Link href="/admin/users">
<a className="bg-blue-500 text-white p-4 rounded-md shadow-lg text-center">
Manage Users
</a>
</Link>
</div>
</div>
);
};
export default AdminDashboard;
3.2 /admin/pets.tsx
A page where admins can perform CRUD operations for pets.
import { useEffect, useState } from 'react';
interface Pet {
id: string;
name: string;
breed: string;
age: number;
location: string;
}
const AdminPets = () => {
const [pets, setPets] = useState<Pet[]>([]);
useEffect(() => {
// Fetch the list of pets
fetch('/api/pets')
.then((res) => res.json())
.then((data) => setPets(data));
}, []);
const handleDelete = async (id: string) => {
await fetch(`/api/pets/${id}`, { method: 'DELETE' });
setPets(pets.filter((pet) => pet.id !== id));
};
return (
<div className="container mx-auto p-8">
<h2 className="text-2xl font-bold mb-4">Manage Pets</h2>
<ul>
{pets.map((pet) => (
<li key={pet.id} className="mb-4 p-4 border rounded-lg">
<h3
className="font-bold">{pet.name}</h3>
<p>Breed: {pet.breed}</p>
<p>Age: {pet.age} years</p>
<p>Location: {pet.location}</p>
<button
onClick={() => handleDelete(pet.id)}
className="bg-red-500 text-white p-2 rounded-md mt-2"
>
Delete Pet
</button>
</li>
))}
</ul>
</div>
);
};
export default AdminPets;
3.3 /admin/applications.tsx
A page where admins can manage adoption applications.
import { useEffect, useState } from 'react';
interface Application {
id: string;
petName: string;
applicantName: string;
status: string;
}
const AdminApplications = () => {
const [applications, setApplications] = useState<Application[]>([]);
useEffect(() => {
// Fetch all adoption applications
fetch('/api/adoptions')
.then((res) => res.json())
.then((data) => setApplications(data));
}, []);
const handleStatusChange = async (id: string, status: string) => {
await fetch(`/api/adoptions/${id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ status }),
});
setApplications(
applications.map((app) =>
app.id === id ? { ...app, status } : app
)
);
};
return (
<div className="container mx-auto p-8">
<h2 className="text-2xl font-bold mb-4">Manage Applications</h2>
<ul>
{applications.map((application) => (
<li key={application.id} className="mb-4 p-4 border rounded-lg">
<h3 className="font-bold">Pet: {application.petName}</h3>
<p>Applicant: {application.applicantName}</p>
<p>Status: {application.status}</p>
<div className="mt-2">
<button
onClick={() => handleStatusChange(application.id, 'Approved')}
className="bg-green-500 text-white p-2 rounded-md mr-2"
>
Approve
</button>
<button
onClick={() => handleStatusChange(application.id, 'Rejected')}
className="bg-red-500 text-white p-2 rounded-md"
>
Reject
</button>
</div>
</li>
))}
</ul>
</div>
);
};
export default AdminApplications;
Conclusion:
This code includes the following pages:
- Pet detail page: Displays individual pet details along with an adoption form.
-
User account management:
- Profile page: Allows users to update their profile.
- Adoption applications: Lists a user's submitted applications.
-
Admin dashboard:
- Manage pets: Allows admins to view and delete pets.
- Manage applications: Allows admins to review, approve, or reject adoption applications.
These components, when integrated with appropriate backend APIs, form the foundation of the platform’s user and admin experience.
Here’s the complete code for the home page, adoption form page, favorites page, about page, as well as the services for API calls, styles, utility functions, and configuration settings based on your provided structure.
Folder Structure:
├── src/
│ ├── pages/
│ │ └── index.tsx # Home page
│ │ └── adopt.tsx # Adoption page (form submission)
│ │ └── favorites.tsx # User's favorite pets (wishlist)
│ │ └── about.tsx # Static About Us page
│ ├── services/ # Services for API calls
│ │ ├── petService.ts # API calls for fetching pet data
│ │ ├── userService.ts # User authentication and profile management services
│ │ └── applicationService.ts # Services for handling adoption applications
│ ├── styles/ # Global styles
│ │ ├── globals.css # Global Tailwind CSS styles
│ │ └── tailwind.css # Custom Tailwind styles (if needed)
│ ├── utils/ # Utility functions
│ │ ├── helpers.ts # General helper functions (e.g., formatting)
│ │ └── validators.ts # Form validation helpers
│ └── config/ # Configuration settings
│ └── api.ts # API base URL and config settings
1. Pages
1.1 index.tsx
(Home Page)
Displays the homepage with a list of adoptable pets.
import PetList from '../components/Pet/PetList';
const HomePage = () => {
return (
<div className="container mx-auto p-8">
<h1 className="text-3xl font-bold mb-8">Welcome to the Pet Adoption Platform</h1>
<p className="mb-4">
Find your perfect pet companion by browsing through our adoptable pets.
</p>
<PetList />
</div>
);
};
export default HomePage;
1.2 adopt.tsx
(Adoption Page)
This page contains the form where users can submit an adoption application.
import { useRouter } from 'next/router';
import AdoptionForm from '../components/Adoption/AdoptionForm';
const AdoptionPage = () => {
const router = useRouter();
const { petId } = router.query;
return (
<div className="container mx-auto p-8">
<h1 className="text-2xl font-bold mb-4">Adopt Your New Best Friend</h1>
{petId ? (
<AdoptionForm petId={petId as string} />
) : (
<p>Select a pet to adopt from our listings.</p>
)}
</div>
);
};
export default AdoptionPage;
1.3 favorites.tsx
(Favorites/Wishlist Page)
Displays the user's favorite pets.
import { useState, useEffect } from 'react';
import PetCard from '../components/Pet/PetCard';
const FavoritesPage = () => {
const [favorites, setFavorites] = useState([]);
useEffect(() => {
// Fetch user's favorite pets
fetch('/api/users/favorites')
.then((res) => res.json())
.then((data) => setFavorites(data));
}, []);
return (
<div className="container mx-auto p-8">
<h1 className="text-2xl font-bold mb-4">Your Favorite Pets</h1>
{favorites.length === 0 ? (
<p>No favorite pets added yet.</p>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
{favorites.map((pet) => (
<PetCard key={pet.id} pet={pet} />
))}
</div>
)}
</div>
);
};
export default FavoritesPage;
1.4 about.tsx
(About Page)
Static page with information about the platform.
const AboutPage = () => {
return (
<div className="container mx-auto p-8">
<h1 className="text-2xl font-bold mb-4">About Us</h1>
<p>
Welcome to our Pet Adoption Platform, where we help find loving homes
for pets in need. Our mission is to connect pets with people looking to
adopt, fostering a community of pet lovers.
</p>
<p className="mt-4">
We work with multiple shelters and rescues to ensure every pet finds a
home. Join us in our journey to help pets find their forever homes!
</p>
</div>
);
};
export default AboutPage;
2. Services
2.1 petService.ts
Handles API calls for fetching pet data.
export const fetchPets = async () => {
const response = await fetch('/api/pets');
if (!response.ok) {
throw new Error('Failed to fetch pets');
}
return response.json();
};
export const fetchPetById = async (id: string) => {
const response = await fetch(`/api/pets/${id}`);
if (!response.ok) {
throw new Error(`Failed to fetch pet with id: ${id}`);
}
return response.json();
};
2.2 userService.ts
Handles user authentication and profile management.
export const fetchUserProfile = async () => {
const response = await fetch('/api/users/me');
if (!response.ok) {
throw new Error('Failed to fetch user profile');
}
return response.json();
};
export const updateUserProfile = async (data: any) => {
const response = await fetch('/api/users/me', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Failed to update profile');
}
return response.json();
};
export const fetchUserFavorites = async () => {
const response = await fetch('/api/users/favorites');
if (!response.ok) {
throw new Error('Failed to fetch user favorites');
}
return response.json();
};
2.3 applicationService.ts
Handles the adoption application process.
export const submitAdoptionApplication = async (data: any) => {
const response = await fetch('/api/adoptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error('Failed to submit adoption application');
}
return response.json();
};
export const fetchUserApplications = async () => {
const response = await fetch('/api/adoptions/me');
if (!response.ok) {
throw new Error('Failed to fetch adoption applications');
}
return response.json();
};
3. Styles
3.1 globals.css
Global styles for the application, including base Tailwind styles.
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom global styles can be added here */
body {
font-family: 'Inter', sans-serif;
}
3.2 tailwind.css
Optional custom Tailwind CSS styles or configurations (if needed).
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom Tailwind components or variants */
4. Utility Functions
4.1 helpers.ts
General utility functions, such as date formatting.
export const formatDate = (date: string) => {
const options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: 'long',
day: 'numeric',
};
return new Date(date).toLocaleDateString(undefined, options);
};
4.2 validators.ts
Form validation helpers for validating user inputs.
export const validateEmail = (email: string) => {
const re = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
return re.test(email);
};
export const validateRequired = (value: string) => {
return value.trim() !== '';
};
5. Configuration
api.ts
Contains the base URL and other API configuration settings.
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3000';
export const getApiUrl = (path: string) => `${apiBaseUrl}${path}`;
Conclusion:
- Pages: The code includes a homepage, adoption form page, favorites page, and an about page.
- Services: There are service functions for handling API calls related to pets, users, and adoption applications.
- Styles: Global styles and Tailwind CSS configurations.
- Utilities: Helper functions for general purposes and
form validation.
- Configuration: Basic API configuration to handle base URLs dynamically.
This setup provides a solid structure for your Pet Adoption Platform using Next.js, Tailwind CSS, and a well-organized service layer.
Here is the full code for the backend folder structure you provided, including common resources, configuration files, and database setup using NestJS and TypeORM.
Backend Folder Structure
├── src/
│ ├── common/ # Shared resources like guards, filters, DTOs, utilities
│ │ ├── dto/ # Shared DTOs
│ │ ├── filters/ # Exception filters
│ │ ├── guards/ # Authorization guards (e.g., roles)
│ │ └── utils/ # Utility functions used across modules
│ ├── config/ # Configuration files (e.g., environment variables)
│ │ ├── config.module.ts # Config service for environment variables
│ │ ├── config.service.ts # Configuration management logic
│ └── database/ # Database-related files (TypeORM, migrations, etc.)
│ ├── entities/ # Database entities for TypeORM
│ │ ├── pet.entity.ts # Pet entity
│ │ ├── user.entity.ts # User entity
│ │ ├── application.entity.ts # Adoption application entity
│ ├── migrations/ # TypeORM migration files
│ └── database.module.ts # Database connection configuration
1. Common Resources
1.1 dto/
(Data Transfer Objects)
-
create-pet.dto.ts
: DTO for creating a pet.
export class CreatePetDto {
readonly name: string;
readonly breed: string;
readonly age: number;
readonly location: string;
}
-
create-user.dto.ts
: DTO for user creation and registration.
export class CreateUserDto {
readonly email: string;
readonly password: string;
readonly name: string;
}
-
create-application.dto.ts
: DTO for adoption applications.
export class CreateApplicationDto {
readonly petId: number;
readonly userId: number;
readonly message: string;
}
1.2 filters/
(Exception Filters)
-
http-exception.filter.ts
: A global exception filter to handle HTTP errors.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
@Catch()
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
1.3 guards/
(Authorization Guards)
-
roles.guard.ts
: A role-based guard to protect routes based on user roles.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role } from '../user/user.entity'; // Assume roles are defined in the User entity
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get<Role[]>('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return roles.includes(user.role);
}
}
1.4 utils/
(Utility Functions)
-
hash.util.ts
: Utility to hash passwords (e.g., using bcrypt).
import * as bcrypt from 'bcrypt';
export const hashPassword = async (password: string): Promise<string> => {
const salt = await bcrypt.genSalt();
return await bcrypt.hash(password, salt);
};
export const comparePassword = async (password: string, hash: string): Promise<boolean> => {
return await bcrypt.compare(password, hash);
};
2. Configuration Files
2.1 config.module.ts
This module provides configuration services (e.g., for reading environment variables).
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env'],
}),
],
providers: [ConfigService],
exports: [ConfigService],
})
export class AppConfigModule {}
2.2 config.service.ts
This service manages the application's configuration settings (e.g., database connections, API keys).
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AppConfigService {
constructor(private configService: ConfigService) {}
get databaseHost(): string {
return this.configService.get<string>('DATABASE_HOST');
}
get databasePort(): number {
return +this.configService.get<string>('DATABASE_PORT');
}
get databaseUser(): string {
return this.configService.get<string>('DATABASE_USER');
}
get databasePassword(): string {
return this.configService.get<string>('DATABASE_PASSWORD');
}
get databaseName(): string {
return this.configService.get<string>('DATABASE_NAME');
}
}
3. Database Configuration and Entities
3.1 pet.entity.ts
(Pet Entity)
Defines the schema for the Pet
table.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Pet {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
breed: string;
@Column()
age: number;
@Column()
location: string;
@Column({ default: true })
isAvailable: boolean;
}
3.2 user.entity.ts
(User Entity)
Defines the schema for the User
table, including authentication fields.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
export enum Role {
User = 'user',
Admin = 'admin',
}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ unique: true })
email: string;
@Column()
password: string;
@Column({ type: 'enum', enum: Role, default: Role.User })
role: Role;
}
3.3 application.entity.ts
(Adoption Application Entity)
Defines the schema for the Application
table.
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Pet } from './pet.entity';
import { User } from './user.entity';
@Entity()
export class Application {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Pet)
pet: Pet;
@ManyToOne(() => User)
user: User;
@Column()
message: string;
@Column({ default: 'pending' })
status: string;
}
4. Migrations
- TypeORM Migration Example
You can generate migration files using the TypeORM CLI. Here's an example of how a migration file might look to create the pets
, users
, and applications
tables.
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateInitialTables1620487951231 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE "user" (
"id" SERIAL NOT NULL,
"name" VARCHAR NOT NULL,
"email" VARCHAR NOT NULL UNIQUE,
"password" VARCHAR NOT NULL,
"role" VARCHAR NOT NULL DEFAULT 'user',
PRIMARY KEY ("id")
)
`);
await queryRunner.query(`
CREATE TABLE "pet" (
"id" SERIAL NOT NULL,
"name" VARCHAR NOT NULL,
"breed" VARCHAR NOT NULL,
"age" INT NOT NULL,
"location" VARCHAR NOT NULL,
"isAvailable" BOOLEAN NOT NULL DEFAULT TRUE,
PRIMARY KEY ("id")
)
`);
await queryRunner.query(`
CREATE TABLE "application" (
"id" SERIAL NOT NULL,
"message" VARCHAR NOT NULL,
"status" VARCHAR NOT NULL DEFAULT 'pending',
"userId" INT,
"petId" INT,
PRIMARY KEY ("id"),
CONSTRAINT "FK_user_application" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE,
CONSTRAINT "FK_pet_application" FOREIGN KEY ("petId") REFERENCES "pet"("id") ON DELETE CASCADE
)
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "application"`);
await queryRunner.query(`DROP TABLE "pet"`);
await queryRunner.query(`DROP TABLE "user"`);
}
}
Running Migrations
Use the following TypeORM CLI commands to generate and run migrations
:
# Generate a migration
typeorm migration:generate -n CreateInitialTables
# Run migrations
typeorm migration:run
5. Database Module
database.module.ts
This module configures the TypeORM connection and imports the entities.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Pet } from './entities/pet.entity';
import { User } from './entities/user.entity';
import { Application } from './entities/application.entity';
@Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'postgres',
host: configService.get<string>('DATABASE_HOST'),
port: +configService.get<number>('DATABASE_PORT'),
username: configService.get<string>('DATABASE_USER'),
password: configService.get<string>('DATABASE_PASSWORD'),
database: configService.get<string>('DATABASE_NAME'),
entities: [Pet, User, Application],
synchronize: true, // Set to `false` in production
}),
inject: [ConfigService],
}),
TypeOrmModule.forFeature([Pet, User, Application]),
],
exports: [TypeOrmModule],
})
export class DatabaseModule {}
Conclusion:
This backend folder structure and code provides a comprehensive setup using NestJS, TypeORM, and PostgreSQL. It includes:
- Common resources: DTOs, filters, guards, and utilities.
- Configuration: Managing environment variables and application settings.
-
Database entities: Entities for
Pet
,User
, andApplication
tables. - Migrations: A TypeORM migration example.
- Database module: Configures TypeORM and PostgreSQL integration.
You can now build a scalable and maintainable backend for your Pet Adoption Platform with this setup.
Here's the full code for the authentication module and user management module that you requested, including the JWT authentication, user management logic, DTOs, and controller files.
Backend Folder Structure
├── src/
│ ├── modules/
│ │ ├── auth/ # Authentication module (JWT)
│ │ │ ├── auth.controller.ts # Auth-related routes (login, register)
│ │ │ ├── auth.module.ts # Auth module declaration
│ │ │ ├── auth.service.ts # Authentication logic
│ │ │ ├── jwt.strategy.ts # JWT Strategy for securing routes
│ │ │ ├── local.strategy.ts # Local strategy (login with email/password)
│ │ │ └── dto/ # DTOs for login, registration, etc.
│ │ ├── users/ # User management module
│ │ │ ├── users.controller.ts # User-related routes
│ │ │ ├── users.module.ts # User module
│ │ │ ├── users.service.ts # User management logic (CRUD, profile updates)
│ │ │ ├── user.entity.ts # User entity (shared with database/entities)
│ │ │ └── dto/ # User-specific DTOs (Data Transfer Objects)
1. Authentication Module (auth/
)
1.1 auth.controller.ts
Handles authentication routes for login and registration.
import { Controller, Post, Body, UseGuards, Request } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalAuthGuard } from './local.strategy';
import { CreateUserDto } from '../users/dto/create-user.dto';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('register')
async register(@Body() createUserDto: CreateUserDto) {
return this.authService.register(createUserDto);
}
@UseGuards(LocalAuthGuard)
@Post('login')
async login(@Request() req) {
return this.authService.login(req.user);
}
}
1.2 auth.module.ts
Declares the Auth Module and imports necessary services like UserService, PassportModule, and JwtModule.
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { JwtStrategy } from './jwt.strategy';
import { LocalStrategy } from './local.strategy';
import { AuthController } from './auth.controller';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET || 'secretKey',
signOptions: { expiresIn: '1h' },
}),
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy],
})
export class AuthModule {}
1.3 auth.service.ts
Handles the business logic for registering and logging in users. Also, it generates JWT tokens.
import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';
import { CreateUserDto } from '../users/dto/create-user.dto';
import { hashPassword, comparePassword } from '../../common/utils/hash.util';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(email: string, pass: string): Promise<any> {
const user = await this.usersService.findByEmail(email);
if (user && await comparePassword(pass, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
async register(createUserDto: CreateUserDto) {
const hashedPassword = await hashPassword(createUserDto.password);
return this.usersService.create({
...createUserDto,
password: hashedPassword,
});
}
}
1.4 jwt.strategy.ts
JWT Strategy that extracts the JWT token from the request and validates it.
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UsersService } from '../users/users.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private usersService: UsersService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET || 'secretKey',
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.username };
}
}
1.5 local.strategy.ts
Local strategy to authenticate users using email and password.
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({ usernameField: 'email' });
}
async validate(email: string, password: string): Promise<any> {
const user = await this.authService.validateUser(email, password);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
1.6 dto/
(Data Transfer Objects for Authentication)
-
login.dto.ts
export class LoginDto {
readonly email: string;
readonly password: string;
}
2. User Management Module (users/
)
2.1 users.controller.ts
Handles user-related routes like fetching user data, updating profile, etc.
import { Controller, Get, Post, Put, Body, Param, UseGuards, Request } from '@nestjs/common';
import { UsersService } from './users.service';
import { JwtAuthGuard } from '../auth/jwt.strategy';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@UseGuards(JwtAuthGuard)
@Get('me')
getProfile(@Request() req) {
return this.usersService.findById(req.user.userId);
}
@UseGuards(JwtAuthGuard)
@Put('me')
updateProfile(@Request() req, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(req.user.userId, updateUserDto);
}
}
2.2 users.module.ts
Declares the User Module and imports the necessary services.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { User } from './user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
2.3 users.service.ts
Handles the business logic for user management like creating, updating, and finding users.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
const user = this.usersRepository.create(createUserDto);
return this.usersRepository.save(user);
}
async findById(id: number): Promise<User> {
return this.usersRepository.findOne(id);
}
async findByEmail(email: string): Promise<User> {
return this.usersRepository.findOne({ where: { email } });
}
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
await this.usersRepository.update(id, updateUserDto);
return this.findById(id);
}
}
2.4 user.entity.ts
Defines the User Entity.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { Role } from './role.enum';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({ unique: true })
email: string;
@Column()
password: string;
@Column({ type: 'enum', enum: Role, default: Role.User })
role: Role;
}
2.5 dto/
(Data Transfer Objects for User Management)
-
create-user.dto.ts
export class CreateUserDto {
readonly name: string;
readonly email: string;
readonly password: string;
}
-
update-user.dto.ts
export class UpdateUserDto {
readonly name?: string;
readonly email?: string;
readonly password?: string;
}
Summary
-
Authentication Module (
auth/
): Contains logic for user registration, login, JWT token handling, and guards for protecting routes. The module includes:- auth.controller.ts: Routes for login and registration.
- auth.service.ts: Business logic for validating users and issuing JWT tokens.
- jwt.strategy.ts: Secures routes using JWT.
- local.strategy.ts: Handles login using email and password.
- DTOs: Defines the data structure for login and registration.
-
User Management Module (
users/
): Handles CRUD operations for users, including creating users, fetching user profiles, and updating user data. The module includes:- users.controller.ts: Routes for creating and managing user data.
- users.service.ts: Business logic for user management.
- user.entity.ts: Defines the database schema for users.
- DTOs: Defines the data structure for creating and updating users.
This structure is highly modular and follows best practices for NestJS and TypeORM, making it easier to extend and maintain.
Here's the full code for the Pets management module and Adoption management module, including the controllers, services, entities, and DTOs for validation.
Backend Folder Structure
├── src/
│ ├── modules/
│ │ ├── pets/ # Pets management module
│ │ │ ├── pets.controller.ts # Routes for handling pet listings
│ │ │ ├── pets.module.ts # Pet module declaration
│ │ │ ├── pets.service.ts # Business logic for pets
│ │ │ ├── pet.entity.ts # Pet entity (shared with database/entities)
│ │ │ └── dto/ # Pet-related DTOs (for validation)
│ │ ├── adoption/ # Adoption management module
│ │ │ ├── adoption.controller.ts # Routes for submitting and managing applications
│ │ │ ├── adoption.module.ts # Adoption module declaration
│ │ │ ├── adoption.service.ts # Business logic for adoption processes
│ │ │ ├── application.entity.ts # Application entity (shared with database/entities)
│ │ │ └── dto/ # Adoption-related DTOs
1. Pets Management Module (pets/
)
1.1 pets.controller.ts
Handles pet-related routes for listing, creating, updating, and deleting pets.
import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { PetsService } from './pets.service';
import { CreatePetDto } from './dto/create-pet.dto';
import { UpdatePetDto } from './dto/update-pet.dto';
@Controller('pets')
export class PetsController {
constructor(private readonly petsService: PetsService) {}
@Get()
findAll() {
return this.petsService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.petsService.findOne(+id);
}
@Post()
create(@Body() createPetDto: CreatePetDto) {
return this.petsService.create(createPetDto);
}
@Put(':id')
update(@Param('id') id: string, @Body() updatePetDto: UpdatePetDto) {
return this.petsService.update(+id, updatePetDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.petsService.remove(+id);
}
}
1.2 pets.module.ts
Declares the Pets Module and imports the necessary services.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PetsService } from './pets.service';
import { PetsController } from './pets.controller';
import { Pet } from './pet.entity';
@Module({
imports: [TypeOrmModule.forFeature([Pet])],
controllers: [PetsController],
providers: [PetsService],
})
export class PetsModule {}
1.3 pets.service.ts
Handles the business logic for managing pets.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Pet } from './pet.entity';
import { CreatePetDto } from './dto/create-pet.dto';
import { UpdatePetDto } from './dto/update-pet.dto';
@Injectable()
export class PetsService {
constructor(
@InjectRepository(Pet)
private petsRepository: Repository<Pet>,
) {}
findAll(): Promise<Pet[]> {
return this.petsRepository.find();
}
findOne(id: number): Promise<Pet> {
return this.petsRepository.findOne(id);
}
create(createPetDto: CreatePetDto): Promise<Pet> {
const pet = this.petsRepository.create(createPetDto);
return this.petsRepository.save(pet);
}
async update(id: number, updatePetDto: UpdatePetDto): Promise<Pet> {
await this.petsRepository.update(id, updatePetDto);
return this.findOne(id);
}
async remove(id: number): Promise<void> {
await this.petsRepository.delete(id);
}
}
1.4 pet.entity.ts
Defines the Pet Entity, representing the structure of the pets in the database.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Pet {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
breed: string;
@Column()
age: number;
@Column()
location: string;
@Column({ default: true })
isAvailable: boolean;
}
1.5 dto/
(Pet-Related DTOs)
-
create-pet.dto.ts
export class CreatePetDto {
readonly name: string;
readonly breed: string;
readonly age: number;
readonly location: string;
}
-
update-pet.dto.ts
export class UpdatePetDto {
readonly name?: string;
readonly breed?: string;
readonly age?: number;
readonly location?: string;
readonly isAvailable?: boolean;
}
2. Adoption Management Module (adoption/
)
2.1 adoption.controller.ts
Handles routes for submitting and managing adoption applications.
import { Controller, Get, Post, Put, Body, Param } from '@nestjs/common';
import { AdoptionService } from './adoption.service';
import { CreateApplicationDto } from './dto/create-application.dto';
import { UpdateApplicationDto } from './dto/update-application.dto';
@Controller('adoptions')
export class AdoptionController {
constructor(private readonly adoptionService: AdoptionService) {}
@Get()
findAll() {
return this.adoptionService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.adoptionService.findOne(+id);
}
@Post()
create(@Body() createApplicationDto: CreateApplicationDto) {
return this.adoptionService.create(createApplicationDto);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateApplicationDto: UpdateApplicationDto) {
return this.adoptionService.update(+id, updateApplicationDto);
}
}
2.2 adoption.module.ts
Declares the Adoption Module and imports necessary services.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AdoptionService } from './adoption.service';
import { AdoptionController } from './adoption.controller';
import { Application } from './application.entity';
@Module({
imports: [TypeOrmModule.forFeature([Application])],
controllers: [AdoptionController],
providers: [AdoptionService],
})
export class AdoptionModule {}
2.3 adoption.service.ts
Handles the business logic for adoption processes.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Application } from './application.entity';
import { CreateApplicationDto } from './dto/create-application.dto';
import { UpdateApplicationDto } from './dto/update-application.dto';
@Injectable()
export class AdoptionService {
constructor(
@InjectRepository(Application)
private applicationsRepository: Repository<Application>,
) {}
findAll(): Promise<Application[]> {
return this.applicationsRepository.find();
}
findOne(id: number): Promise<Application> {
return this.applicationsRepository.findOne(id);
}
create(createApplicationDto: CreateApplicationDto): Promise<Application> {
const application = this.applicationsRepository.create(createApplicationDto);
return this.applicationsRepository.save(application);
}
async update(id: number, updateApplicationDto: UpdateApplicationDto): Promise<Application> {
await this.applicationsRepository.update(id, updateApplicationDto);
return this.findOne(id);
}
}
2.4 application.entity.ts
Defines the Application Entity for adoption applications.
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Pet } from '../pets/pet.entity';
import { User } from '../users/user.entity';
@Entity()
export class Application {
@PrimaryGeneratedColumn()
id: number;
@ManyToOne(() => Pet)
pet: Pet;
@ManyToOne(() => User)
user: User;
@Column()
message: string;
@Column({ default: 'pending' })
status: string;
}
2.5 dto/
(Adoption-Related DTOs)
-
create-application.dto.ts
export class CreateApplicationDto {
readonly petId: number;
readonly userId: number;
readonly message: string;
}
-
update-application.dto.ts
export class UpdateApplicationDto {
readonly status?: string;
}
Summary
-
Pets Management Module (
pets/
): Handles all
operations related to pets (listing, creating, updating, deleting).
- Controller: Defines routes for managing pets.
- Service: Handles business logic for managing pets.
- Entity: Represents the pet in the database.
-
DTOs: For validation of data (create/update pet).
-
Adoption Management Module (
adoption/
): Manages the submission and processing of adoption applications.
-
Adoption Management Module (
Controller: Defines routes for submitting and managing applications.
Service: Contains business logic for processing adoption applications.
Entity: Represents an adoption application in the database.
DTOs: For validation of data (create/update application).
These modules provide a structured and scalable approach to managing pets and adoption applications in a NestJS application. You can further extend these modules as needed for your Pet Adoption Platform.
Here’s the full code for the notifications module (handling email, SMS, and push notifications), the admin module (admin-specific functionality for managing pets and adoption applications), and the main setup of the application including the root module (app.module.ts
) and the entry point (main.ts
).
Backend Folder Structure
├── src/
│ ├── modules/
│ │ ├── notifications/ # Notification handling (email, SMS, push notifications)
│ │ │ ├── notifications.module.ts # Notifications module declaration
│ │ │ ├── notifications.service.ts # Notification logic (for emails, push notifications)
│ │ │ ├── email.service.ts # Email sending service
│ │ │ └── push.service.ts # Push notification logic (Firebase integration)
│ │ ├── admin/ # Admin-specific functionality
│ │ │ ├── admin.controller.ts # Routes for admin operations (approving adoptions, managing pets)
│ │ │ ├── admin.module.ts # Admin module declaration
│ │ │ └── admin.service.ts # Admin-specific business logic
│ ├── app.module.ts # Root application module
│ ├── main.ts # Entry point of the application
1. Notifications Module (notifications/
)
This module handles notification logic, including sending emails and push notifications (e.g., using Firebase).
1.1 notifications.module.ts
Declares the Notifications Module, importing the necessary services for sending email and push notifications.
import { Module } from '@nestjs/common';
import { NotificationsService } from './notifications.service';
import { EmailService } from './email.service';
import { PushService } from './push.service';
@Module({
providers: [NotificationsService, EmailService, PushService],
exports: [NotificationsService], // Exporting so other modules can use it
})
export class NotificationsModule {}
1.2 notifications.service.ts
Handles the logic for sending notifications, whether by email or push.
import { Injectable } from '@nestjs/common';
import { EmailService } from './email.service';
import { PushService } from './push.service';
@Injectable()
export class NotificationsService {
constructor(
private emailService: EmailService,
private pushService: PushService,
) {}
// Method to send email notifications
async sendEmailNotification(to: string, subject: string, message: string): Promise<void> {
await this.emailService.sendEmail(to, subject, message);
}
// Method to send push notifications
async sendPushNotification(token: string, message: string): Promise<void> {
await this.pushService.sendPushNotification(token, message);
}
}
1.3 email.service.ts
This service handles sending emails (e.g., using SendGrid, Nodemailer, or other email services).
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
@Injectable()
export class EmailService {
private transporter = nodemailer.createTransport({
service: 'gmail', // Use your email service (Gmail, SendGrid, etc.)
auth: {
user: process.env.EMAIL_USER, // From environment variables
pass: process.env.EMAIL_PASS, // From environment variables
},
});
async sendEmail(to: string, subject: string, message: string): Promise<void> {
const mailOptions = {
from: process.env.EMAIL_USER,
to,
subject,
text: message,
};
await this.transporter.sendMail(mailOptions);
}
}
1.4 push.service.ts
This service handles sending push notifications (e.g., using Firebase Cloud Messaging).
import { Injectable } from '@nestjs/common';
import * as admin from 'firebase-admin'; // Firebase Admin SDK for push notifications
@Injectable()
export class PushService {
constructor() {
const serviceAccount = require(process.env.FIREBASE_CREDENTIALS_PATH);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
}
async sendPushNotification(token: string, message: string): Promise<void> {
const payload = {
notification: {
title: 'Adoption Update',
body: message,
},
};
await admin.messaging().sendToDevice(token, payload);
}
}
2. Admin Module (admin/
)
This module contains the admin-specific functionality, allowing admins to manage pets and adoption applications.
2.1 admin.controller.ts
Defines routes for the admin to manage pets and approve or reject adoption applications.
import { Controller, Get, Put, Param, Body } from '@nestjs/common';
import { AdminService } from './admin.service';
@Controller('admin')
export class AdminController {
constructor(private readonly adminService: AdminService) {}
// Fetch all adoption applications
@Get('applications')
getAllApplications() {
return this.adminService.findAllApplications();
}
// Approve or reject an application
@Put('applications/:id')
updateApplicationStatus(
@Param('id') id: string,
@Body('status') status: string,
) {
return this.adminService.updateApplicationStatus(+id, status);
}
// Fetch all pets
@Get('pets')
getAllPets() {
return this.adminService.findAllPets();
}
}
2.2 admin.module.ts
Declares the Admin Module and imports the necessary services.
import { Module } from '@nestjs/common';
import { AdminController } from './admin.controller';
import { AdminService } from './admin.service';
import { PetsModule } from '../pets/pets.module';
import { AdoptionModule } from '../adoption/adoption.module';
@Module({
imports: [PetsModule, AdoptionModule], // Admin can manage both pets and applications
controllers: [AdminController],
providers: [AdminService],
})
export class AdminModule {}
2.3 admin.service.ts
Contains the business logic for handling admin-specific operations such as approving adoption applications or managing pets.
import { Injectable } from '@nestjs/common';
import { PetsService } from '../pets/pets.service';
import { AdoptionService } from '../adoption/adoption.service';
@Injectable()
export class AdminService {
constructor(
private petsService: PetsService,
private adoptionService: AdoptionService,
) {}
// Fetch all adoption applications
async findAllApplications() {
return this.adoptionService.findAll();
}
// Update application status
async updateApplicationStatus(id: number, status: string) {
return this.adoptionService.update(id, { status });
}
// Fetch all pets
async findAllPets() {
return this.petsService.findAll();
}
}
3. Root Application Setup
3.1 app.module.ts
The root application module that imports all feature modules like AdminModule, NotificationsModule, PetsModule, AdoptionModule, etc.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PetsModule } from './modules/pets/pets.module';
import { AdoptionModule } from './modules/adoption/adoption.module';
import { NotificationsModule } from './modules/notifications/notifications.module';
import { AdminModule } from './modules/admin/admin.module';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }), // Load environment variables globally
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DATABASE_HOST,
port: parseInt(process.env.DATABASE_PORT, 10),
username: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME,
autoLoadEntities: true,
synchronize: true, // Set to `false` in production
}),
PetsModule,
AdoptionModule,
NotificationsModule,
AdminModule,
],
})
export class AppModule {}
3.2 main.ts
The entry point of the NestJS application, where the application is bootstrapped.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Enable CORS
app.enableCors();
// Use a global validation pipe to validate incoming data
app.useGlobalPipes(new ValidationPipe());
await app.listen(process.env.PORT || 3000);
}
bootstrap();
Summary
-
Notifications Module (
notifications/
):- NotificationsService: Manages sending both email and push notifications.
- EmailService: Uses Nodemailer or other services (e.g., Gmail, SendGrid) to send emails.
- PushService: Sends push notifications via Firebase Cloud Messaging.
-
Admin Module (
admin/
):- AdminController: Handles routes for approving or rejecting adoption applications and managing pets.
- AdminService: Contains business logic for admin-specific tasks.
AppModule (
app.module.ts
): The root module of the application that imports other feature modules like AdminModule,
NotificationsModule, and PetsModule.
- Main.ts: The entry point of the NestJS application, where the app is initialized and listens on a specific port.
This structure ensures scalability, modularity, and separation of concerns across different functionalities of the Pet Adoption Platform, with features like notification handling, admin-specific operations, and an organized root application setup.
If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!
Disclaimer: This content is generated by AI.
Posted on October 10, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 27, 2024