Building a Blog API with Dapper and MongoDB in .NET Core 9

In today’s development landscape, building efficient and scalable APIs is crucial for modern applications. This tutorial will guide you through creating a simple blog API using .NET Core 9, Dapper, and MongoDB. This combination provides a lightweight ORM with the flexibility of a NoSQL database, enabling efficient data operations.
Prerequisites
Before we begin, ensure you have the following:
- .NET SDK (version 9.0 or higher)
- A MongoDB instance running locally or accessible via a connection string (you can use MongoDB Atlas for a cloud-based solution)
- Basic knowledge of C# and ASP.NET Core
- A code editor (like Visual Studio or Visual Studio Code)
Step 1: Set Up Your Project
1. Create a New ASP.NET Core Web API Project
Open your terminal or command prompt and create a new project:
dotnet new webapi -n BlogApi
cd BlogApi
2. Add Required NuGet Packages
Next, add the necessary NuGet packages for MongoDB and Dapper. Run the following commands in your terminal:
dotnet add package MongoDB.Driver
dotnet add package Dapper
Step 2: Create the Blog Post Model
Create a model class that represents a blog post. This class will define the structure of the data we will be working with. In the Models
directory, create a file named BlogPost.cs
:
using System;
namespace BlogApi.Models
{
public class BlogPost
{
public string Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; } // To track updates
}
}
Step 3: Set Up MongoDB Context
Next, create a class to manage the MongoDB connection and operations. In the Data
directory, create a file named MongoDbContext.cs
:
using MongoDB.Driver;
namespace BlogApi.Data
{
public class MongoDbContext
{
private readonly IMongoDatabase _database;
public MongoDbContext(string connectionString, string databaseName)
{
var client = new MongoClient(connectionString);
_database = client.GetDatabase(databaseName);
}
public IMongoCollection<BlogPost> BlogPosts => _database.GetCollection<BlogPost>("BlogPosts");
}
}
Best Practices for Connection Strings
- Keep your connection string secure. Consider using environment variables or a configuration file to store sensitive data.
- Use MongoDB Atlas for cloud-based solutions and take advantage of their features like backups and scaling.
Step 4: Create a Repository
To handle data operations, create a repository that encapsulates the logic for accessing blog posts. In the Repositories
directory, create a file named BlogPostRepository.cs
:
using Dapper;
using MongoDB.Bson;
using MongoDB.Driver;
using BlogApi.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace BlogApi.Repositories
{
public class BlogPostRepository
{
private readonly MongoDbContext _context;
public BlogPostRepository(MongoDbContext context)
{
_context = context;
}
public async Task<IEnumerable<BlogPost>> GetAllPostsAsync()
{
var posts = await _context.BlogPosts.Find(_ => true).ToListAsync();
return posts;
}
public async Task<BlogPost> GetPostByIdAsync(string id)
{
var post = await _context.BlogPosts.Find(p => p.Id == id).FirstOrDefaultAsync();
return post;
}
public async Task AddPostAsync(BlogPost post)
{
post.CreatedAt = DateTime.UtcNow; // Set creation date
post.UpdatedAt = DateTime.UtcNow; // Set initial update date
await _context.BlogPosts.InsertOneAsync(post);
}
public async Task UpdatePostAsync(string id, BlogPost post)
{
post.UpdatedAt = DateTime.UtcNow; // Update the timestamp
await _context.BlogPosts.ReplaceOneAsync(p => p.Id == id, post);
}
public async Task DeletePostAsync(string id)
{
await _context.BlogPosts.DeleteOneAsync(p => p.Id == id);
}
}
}
Enhancing the Repository
- The
UpdatePostAsync
method allows you to modify existing blog posts while keeping track of updates. - The
DeletePostAsync
method provides functionality to remove posts.
Step 5: Configure Services in Startup
Now we need to register our MongoDB context and repository in the Startup.cs
file. Modify the ConfigureServices
method:
using BlogApi.Data;
using BlogApi.Repositories;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Configure MongoDB context
services.AddSingleton<MongoDbContext>(provider =>
{
var connectionString = Environment.GetEnvironmentVariable("MONGODB_CONNECTION_STRING");
var databaseName = "BlogDatabase"; // Update with your database name
return new MongoDbContext(connectionString, databaseName);
});
services.AddScoped<BlogPostRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Storing Connection Strings Securely
- Use environment variables to store sensitive information such as your MongoDB connection string.
- Access the connection string using
Environment.GetEnvironmentVariable()
in your code.
Step 6: Create a Controller
To expose our API endpoints, create a controller for managing blog posts. In the Controllers
directory, create a file named BlogPostsController.cs
:
using Microsoft.AspNetCore.Mvc;
using BlogApi.Models;
using BlogApi.Repositories;
using System.Collections.Generic;
using System.Threading.Tasks;
[Route("api/[controller]")]
[ApiController]
public class BlogPostsController : ControllerBase
{
private readonly BlogPostRepository _repository;
public BlogPostsController(BlogPostRepository repository)
{
_repository = repository;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<BlogPost>>> GetPosts()
{
var posts = await _repository.GetAllPostsAsync();
return Ok(posts);
}
[HttpGet("{id}")]
public async Task<ActionResult<BlogPost>> GetPost(string id)
{
var post = await _repository.GetPostByIdAsync(id);
if (post == null)
{
return NotFound();
}
return Ok(post);
}
[HttpPost]
public async Task<ActionResult> AddPost([FromBody] BlogPost post)
{
await _repository.AddPostAsync(post);
return CreatedAtAction(nameof(GetPost), new { id = post.Id }, post);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdatePost(string id, [FromBody] BlogPost post)
{
var existingPost = await _repository.GetPostByIdAsync(id);
if (existingPost == null)
{
return NotFound();
}
await _repository.UpdatePostAsync(id, post);
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeletePost(string id)
{
var existingPost = await _repository.GetPostByIdAsync(id);
if (existingPost == null)
{
return NotFound();
}
await _repository.DeletePostAsync(id);
return NoContent();
}
}
Enhancements in the Controller
- The controller now includes methods for updating and deleting blog posts, expanding the API’s functionality.
- HTTP response codes are returned appropriately for different scenarios (e.g., 404 for not found, 204 for no content).
Step 7: Testing the API
Running Your Application
Make sure your MongoDB server is running, then execute your application:
dotnet run
Testing Endpoints with Postman
You can use Postman to interact with your API. Here are some example requests:
Get all posts:
- Method:
GET
- URL:
http://localhost:5000/api/blogposts
Get a specific post by ID:
- Method:
GET
- URL:
http://localhost:5000/api/blogposts/{id}
Add a new post:
- Method:
POST
- URL:
http://localhost:5000/api/blogposts
- Body (JSON):
{
"title": "My First Blog Post",
"content": "This is the content of my first blog post."
}
Update an existing post:
- Method:
PUT
- URL:
http://localhost:5000/api/blogposts/{id}
- Body (JSON):
{
"title": "Updated Blog Post Title",
"content": "Updated content for the blog post."
}
Delete a post:
- Method:
DELETE
- URL:
http://localhost:5000/api/blogposts/{id}
Conclusion
In this tutorial, we built a fully functional blog API using .NET Core 9, Dapper, and MongoDB. This setup allows for efficient data handling and provides a solid foundation for building more complex applications.
Future Enhancements
You can expand this API further by adding features such as:
- Authentication and Authorization: Implement JWT or OAuth2 for secure access.
- Pagination: Add pagination to the
GetPosts
method to handle large datasets efficiently. - Search Functionality: Allow users to search for blog posts by title or content.
- Input Validation: Implement validation to ensure data integrity.
- Testing: Write unit tests to ensure your API behaves as expected.
With these enhancements, you can create a robust and scalable blogging platform. Happy coding!