Feature List | User Stories | Database Schema | Wireframes
Project Overview | Project Journey | Live Link | Implemented Technologies | Endpoint Documentation | User Endpoints | Notebook Endpoints | Entries Endpoints | Posts Endpoints | Comments Endpoints | Features | Future Implimentation Goals | Contact
PenCrafted is a creative writing website designed to inspire and support writers of all levels. Inspired by the functionality of EverNote, PenCrafted offers a user-friendly platform where you can craft and organize your writing projects. Whether you want to keep your work private or share it with the PenCrafted community for constructive criticism, PenCrafted provides the tools you need to enhance your writing journey.
PenCrafted is my Capstone project for AppAcademy, developed within a challenging two-week timeframe. Throughout this project, I aimed to create a platform that nurtures creativity and community among writers. While time constraints prevented me from implementing all aspects of my vision, this project successfully offers an intuitive user experience with a clean and elegant design. I am committed to continuing the development of PenCrafted and enhancing its features in the future.
https://capstone-project-lm4v.onrender.com
Successful Response:
{
"id": "INTEGER",
"name": "STRING",
"username": "STRING",
"profile_image": "STRING",
"theme": "STRING"
}
Error Response:
{
"message": "Unauthorized"
}
Successful Response:
{
"id": "INTEGER",
"name": "STRING",
"username": "STRING",
"profile_image": "STRING",
"theme": "STRING"
}
Error Response:
{
"password": [
"No such user exists."
],
"username": [
"Username provided not found.",
"Password was incorrect."
]
}
Response:
{
"message": "Unauthorized"
}
Successful Response:
{
"message": "User logged out"
}
Successful Response:
{
"id": "INTEGER",
"name": "STRING",
"username": "STRING",
"profile_image": "STRING",
"theme": "STRING"
}
Error Response:
{
"name": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
"password": [
"This field is required",
"Field must be at least 8 characters long."
],
"username": [
"This field is required",
"Field must be between 8 and 50 characters long.",
"Username is already in use."
]
}
Error Response:
{
"message": "Unauthorized"
}
Successful Response:
{
"users" : [
{
"id": "INTEGER",
"name": "STRING",
"username": "STRING",
"profile_image": "STRING",
"theme": "STRING"
},
{
"id": "INTEGER",
"name": "STRING",
"username": "STRING",
"profile_image": "STRING",
"theme": "STRING"
},
]
}
Successful Response:
{
{
"id": "INTEGER",
"name": "STRING",
"username": "STRING",
"profile_image": "STRING",
"theme": "STRING"
},
}
Successful Response:
{
"notebooks" : [
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"about": "STRING",
"created_at": "DATE"
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"about": "STRING",
"created_at": "DATE"
},
]
}
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"about": "STRING",
"created_at": "DATE"
}
Request: POST api/notebooks/new
Purpose: Creates a new notebook within the database with the provided inputs and returns a dictionary of the new notebook.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"about": "STRING",
"created_at": "DATE"
}
Error Response:
{
"name": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
"password": [
"This field is required",
"Field must be at least 8 characters long."
],
"username": [
"This field is required",
"Field must be between 8 and 50 characters long.",
"Username is already in use."
]
}
Request: POST api/notebooks/:notebookId/edit
Purpose: Edits a specific notebook by its id and returns the updated version as a dictionary. Updates the notebook in the database.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"about": "STRING",
"created_at": "DATE"
}
Error Response:
{
"name": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
"password": [
"This field is required",
"Field must be at least 8 characters long."
],
"username": [
"This field is required",
"Field must be between 8 and 50 characters long.",
"Username is already in use."
]
}
Successful Response:
{
"message": "Notebook has been successfully deleted"
}
Successful Response:
{
[
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
]
}
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
Request: POST api/entries/new
Purpose: Creates a new entry within the database with the provided inputs and returns a dictionary of the new entry, along with an empty comments array.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
},
Error Response:
{
"name": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
"password": [
"This field is required",
"Field must be at least 8 characters long."
],
"username": [
"This field is required",
"Field must be between 8 and 50 characters long.",
"Username is already in use."
]
}
Request: POST api/entries/:entryId/edit
Purpose: Edits a specific entry by its id and returns the updated version as a dictionary. Updates the entry in the database.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
Error Response:
{
"name": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
"password": [
"This field is required",
"Field must be at least 8 characters long."
],
"username": [
"This field is required",
"Field must be between 8 and 50 characters long.",
"Username is already in use."
]
}
Successful Response:
{
"message": "Entry has been successfully deleted"
}
Successful Response:
{
[
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
]
}
Successful Response:
{
[
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
]
}
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
}
Request: POST api/posts/new
Purpose: Creates a new post within the database with the provided inputs and returns an updated dictionary of the posted entry (sets boolean is_posted to true) with the new post attached.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"name": "STRING",
"content": "TEXT",
"is_public": "BOOLEAN - default(false)",
"created_at": "DATE",
"updated_at": "DATE",
"comments": [
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
},
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
],
},
Error Response:
{
"name": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
"password": [
"This field is required",
"Field must be at least 8 characters long."
],
"username": [
"This field is required",
"Field must be between 8 and 50 characters long.",
"Username is already in use."
]
}
Request: POST api/posts/:postId/edit
Purpose: Edits a specific posts internal information, just message, and returns the updated post.
Successful Response:
{
"post": {
"id": "INTEGER",
"entry_id": "INTEGER",
"message": "STRING",
"created_at": "DATE",
}
},
Error Response:
{
"message": [
"Message can not be longer that 400 characters"
],
}
Successful Response:
{
"message": "Entry has been successfully deleted"
}
Request: POST api/comments/new
Purpose: Creates a new comment within the database with the provided inputs and returns the new comment as a dictionary.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
Error Response:
{
"comment": [
"This field is required",
"Field must be between 4 and 50 characters long."
],
}
Request: POST api/comments/:commentId/edit
Purpose: Edits a comment by id with the provided information and returns the updated comment as a dictionary.
Successful Response:
{
"id": "INTEGER",
"user_id": "INTEGER",
"entry_id": "INTEGER",
"comment": "STRING",
"created_at": "DATE",
}
Error Response:
{
"message": [
"Comment can not be longer that 600 characters"
],
}
Request: GET api/comments/:commentId/delete
Purpose: Deletes a comment by its id only if the current user is the creator of the comment or the creator of the post which the comment is attached to, removes the comment from the database and returns a success response
Successful Response:
{
"message": "Comment has been successfully deleted"
}
Error Response:
{
"message": [
"You are not the owner of this comment, nor are you the creator of the post it is posted to"
],
}