BlogIT-API is a C# library that contains all the server side logic for performing CRUD operations related to blog posts users make, credentialing around the application, and more!
This project makes use of Azure Key Vault storage. This allows me to store all sensitive information in the cloud, such as database connection strings which contains a username and password, and keep that out of the application.
The following packages are needed for the core function of the application
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.AspNetCore.OpenApi
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
- Swashbuckle.AspNetCore.SwaggerUI
The following packages are needed for Jwt Authentication
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.IdentityModel.Tokens
- System.IdentityModel.Tokens.Jwt
The following packages are needed to hook into Azure Key Vault
- Azure.Extensions.AspNetCore.Configuration.Secrets
- Azure.Identity
- Azure.Security.KeyVault.Secrets
- .NET 8 SDK
- SQL Server
- Azure Blob Storage account (for image uploads)
-
Clone the repository
git clone https://github.com/alexfitzkane-02/BlogIT-API.git cd BlogIT-API -
Configure
appsettings.json{ "ConnectionStrings": { "DefaultConnection": "Your SQL Server connection string" }, "Jwt": { "Key": "your-secret-key", "Issuer": "your-issuer", "Audience": "your-audience" }, "AzureBlobStorage": { "ConnectionString": "your-azure-blob-connection-string", "ContainerName": "your-container-name" } } -
Apply migrations
dotnet ef database update
-
Run the API
dotnet run
-
Explore with Swagger Navigate to
https://localhost:{port}/swaggerfor interactive API docs.
Protected routes require a Bearer token in the Authorization header:
Authorization: Bearer <your_jwt_token>
Routes marked with 🔒 require authentication with the Writer role.
Get all authors. (Public)
Response 200 OK:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Alex Kane",
"urlHandle": "alex-kane"
}
]Get a single author by ID. (Public)
Response 200 OK:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Alex Kane",
"urlHandle": "alex-kane"
}Response 404 Not Found: "Author not found"
Create a new author.
Request body:
{
"name": "Alex Kane",
"urlHandle": "alex-kane"
}Response 200 OK:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Alex Kane",
"urlHandle": "alex-kane"
}Update an existing author.
Request body:
{
"name": "Alexander Kane",
"urlHandle": "alexander-kane"
}Response 200 OK:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Alexander Kane",
"urlHandle": "alexander-kane"
}Response 404 Not Found: "Author not found"
Delete an author by ID.
Response 200 OK: Returns the deleted author object.
Response 404 Not Found: "Author not found"
Get all categories. (Public)
Response 200 OK:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Technology",
"urlHandle": "technology"
}
]Get a single category by ID. (Public)
Response 200 OK:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Technology",
"urlHandle": "technology"
}Response 404 Not Found: "Category not found"
Create a new category.
Request body:
{
"name": "Technology",
"urlHandle": "technology"
}Response 200 OK:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Technology",
"urlHandle": "technology"
}Update an existing category.
Request body:
{
"name": "Tech & Science",
"urlHandle": "tech-and-science"
}Response 200 OK: Returns the updated category object.
Response 404 Not Found: "Category not found"
Delete a category by ID.
Response 200 OK: Returns the deleted category object.
Response 404 Not Found: "Category not found"
Get all blog posts. (Public)
Response 200 OK:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "My First Post",
"description": "This is the content of the post.",
"author": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Alex Kane",
"urlHandle": "alex-kane"
},
"featuredImageUrl": "https://yourstorage.blob.core.windows.net/images/example.jpg",
"urlHandle": "my-first-post",
"isVisible": true,
"createdTimeStamp": "2025-01-01",
"lastEditTimeStamp": "2025-01-02",
"categories": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Technology",
"urlHandle": "technology"
}
]
}
]Get a single blog post by GUID. (Public)
Response 200 OK: Returns a single blog post object (same shape as above).
Response 404 Not Found: "Blog post was not found"
Get a single blog post by its URL handle. (Public)
Example: GET /api/blog/my-first-post
Response 200 OK: Returns a single blog post object.
Response 404 Not Found: "Blog post was not found"
Create a new blog post.
Request body:
{
"title": "My New Post",
"description": "This is the content of the new post.",
"author": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"featuredImageUrl": "https://yourstorage.blob.core.windows.net/images/example.jpg",
"urlHandle": "my-new-post",
"isVisible": true,
"categories": [
"3fa85f64-5717-4562-b3fc-2c963f66afa6"
]
}
⚠️ authormust be a valid existing Author GUID.categoriesis a list of existing Category GUIDs.
Response 200 OK: Returns the created blog post object.
Response 500 Internal Server Error: "An error occurred while creating the blog post."
Update an existing blog post.
Request body: Same shape as POST /api/blog.
Response 200 OK: Returns the updated blog post object.
Response 404 Not Found: Author, category, or post not found.
Delete a blog post by ID.
Response 200 OK: Returns the deleted blog post object.
Response 404 Not Found: "Blog not found"
Get all uploaded images. (Public)
Response 200 OK:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "example",
"fileExtension": ".jpg",
"title": "My Image",
"url": "https://yourstorage.blob.core.windows.net/images/example.jpg",
"dateCreated": "2025-01-01T12:00:00Z"
}
]Upload an image to Azure Blob Storage. (Public)
Request: multipart/form-data
| Field | Type | Description |
|---|---|---|
file |
File | Image file (.jpg, .jpeg, .png only, max 10MB) |
fileName |
string | Desired file name |
title |
string | Display title for the image |
Response 200 OK:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"fileName": "example",
"fileExtension": ".jpg",
"title": "My Image",
"url": "https://yourstorage.blob.core.windows.net/images/example.jpg",
"dateCreated": "2025-01-01T12:00:00Z"
}Response 400 Bad Request: Unsupported file type or file exceeds 10MB.
- Framework: ASP.NET Core (.NET 8)
- Database: SQL Server with Entity Framework Core
- Authentication: JWT Bearer tokens
- Image Storage: Azure Blob Storage
- API Docs: Swagger / Swashbuckle
BlogIT/
├── Controllers/ # API controllers (Blog, Category, Author, Images)
├── Models/
│ ├── Domain/ # Entity models
│ └── Dto/ # Data Transfer Objects
├── Repositories/
│ ├── Interface/ # Repository interfaces
│ └── Implementation/ # Repository implementations
├── Data/ # EF Core DbContext
└── Program.cs
When running the application locally on your machine, you need to make sure you have Azure CLI installed locally. To check if you already have it installed you can perform the following CMD promt:
az versionIf you do not have it, you can install it through PowerShell with the following command:
winget install --exact --id Microsoft.AzureCLIOnce you confirm the Azure CLI is installed, restart Visual Studio and login through the Package Manager Console with the following command:
az loginIn certain scenarios you might need to append your tenantID to the command:
az login --tenant {insert tenantID}Please make sure to update tests as appropriate.