Share this post
  

Create a React-based app with ASP.NET Minimal APIs and MongoDB storage

I’ve been using React for while by mixing it up with .NET stuff and enjoying it a lot. For this post, I decided to combine them along Compose containers, ASP.NET Minimal APIs and MongoDB support!

Using React

I use React the tech behind my blog (mostly NodeJS/Jamstack client-side generated which I migrated recently from existing code that has been around ~2015) both for the client and server side rendering.

So this time I decided to set a sample to combine React and .NET in a sample app and ended-up packing it as Pull-Request #248 for the Awesome-Compose repo from Docker in GitHub, following same structure I did for previous .NET contributions into that repo.

If you are interested in previous posts about Awesome-Compose, either curious about Docker-izing .NET 5/6 apps or using GitHub Codespaces for development you can browse them from here and here.

The code for this post is also available from my GitHub under this repo:

https://github.com/stvansolano/codebox-react-dotnet-mongodb

So, let’s get started! A few things you may need to install:

  • Docker Compose
  • .NET 6 SDK
  • NodeJS

Side-note: I didn’t really have to install anything while developing this on my local as I was using GitHub Codespaces with my some tailored devcontainer setup, so I can focus on development!

1
<Image width="360" height="202" src="/2022/06/20/Creating-a-react-app-with-AspNet-Minimal-APIs-and-MongoDb-CosmosDb/preview.webp" alt="Preview" title="Preview" />

1) Creating the /frontend: A simple TODO app.

For this sample, we will use react-scripts scaffold for creating a basic app and use a very simple, still classic sample for having a TODO app in order to focus on JavaScript, NodeJS/Npx stuff. For this sample, I also re-used pretty much of the React sample from Awesome-Compose repo, which is fairly common and straightforward:

1
<Image width="183" height="400" src="/2022/06/20/Creating-a-react-app-with-AspNet-Minimal-APIs-and-MongoDb-CosmosDb/frontend-structure.webp" alt="Frontend structure" title="Frontend structure" />

In order to consume our API later from ASP.NET, let’s add a NGINX file for taking care of the API calls later from our ASP.NET Web API. Things will run on their own NGINX-based container so the backend is de-coupled from the frontend.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server {
listen 80;
server_name localhost;

server_tokens off;
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-Permitted-Cross-Domain-Policies master-only;
add_header Referrer-Policy same-origin;
add_header Expect-CT 'max-age=60';

location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}

location /api {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_pass http://backend:8000;
proxy_redirect default;
}
}

Important: The .NET CLI and also Visual Studio provide a great set of pre-defined templates that also include React, Angular and more great stuff, which I encourage to give it a try. Also, pretty much of this tutorial can be also take .NET on the frontend, with the help of amazing Blazor 💖✨

2) Now it’s time for .NET in the /backend: Set up Minimal API for MongoDB

Here things become more interesting. By scaffolding a new Web API from .NET 6, we can implement a very-simple, still small program to call MongoDB collection and support some basic operations for our API, and we can add Swagger+OpenAPI support with a few lines of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using System;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using Models;
using Swashbuckle.AspNetCore.SwaggerGen;

var builder = WebApplication.CreateBuilder(args);

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

string connectionString = builder.Configuration.GetConnectionString("DocumentDbConnection");
string databaseName = builder.Configuration.GetConnectionString("DocumentDbName") ?? "BackendMongoDb";
string collectionName = builder.Configuration.GetConnectionString("DocumentCollectionName") ?? "ToDos";

builder.Services.AddTransient<MongoClient>((_provider) => new MongoClient(connectionString));

var app = builder.Build();

var isSwaggerEnabledFromConfig = bool.TrueString.Equals(builder.Configuration["EnableSwagger"] ?? "", StringComparison.OrdinalIgnoreCase);
if (isSwaggerEnabledFromConfig)
{
Console.WriteLine("Swagger enabled via appsettings.json");
}

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment() || isSwaggerEnabledFromConfig)
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.MapGet("/api/todos", async (MongoClient connection) =>
{
try
{
var database = connection.GetDatabase(databaseName);
var collection = database.GetCollection<ToDo>(collectionName);
var results = await collection.Find(_ => true).ToListAsync().ConfigureAwait(false);

return Results.Ok(results);
}
catch (Exception ex)
{
return Results.Problem(detail: ex.ToString());
}
});

app.MapGet("/api/todos/{id}", async (string id, MongoClient connection) =>
{
try
{
var database = connection.GetDatabase(databaseName);
var collection = database.GetCollection<ToDo>(collectionName);
var result = await collection.FindAsync(record => record.Id == id).ConfigureAwait(false) as ToDo;

if (result is null)
{
return Results.NotFound();
}

return Results.Created($"/todoitems/{result.Id}", result);
}
catch (Exception ex)
{
return Results.Problem(detail: ex.ToString());
}
});

app.MapPost("/api/todos", async (ToDo record, MongoClient connection) =>
{
try
{
var database = connection.GetDatabase(databaseName);
var collection = database.GetCollection<ToDo>(collectionName);
await collection.InsertOneAsync(record).ConfigureAwait(false);

return Results.Created($"/api/todos/{record.Id}", record);
}
catch (Exception ex)
{
return Results.Problem(detail: ex.ToString());
}
});

app.Run();

Here is a screenshot of the backend when opening the /swagger endpoint:

1
<Image width="400" height="377" src="/2022/06/20/Creating-a-react-app-with-AspNet-Minimal-APIs-and-MongoDb-CosmosDb/swagger.webp" alt="Backend swagger" title="Backend swagger" />

3) Oh! Yes, MongoDB. Let’s Compose it and wrap up with .NET!

Last but not least, let’s have the Frontend in place, connect it to the Backend and store our To-Dos in MongoDB.

We can use Compose services here by just grabbing the container images and set things up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 services:
frontend:
build:
context: frontend
ports:
- 80:80
volumes:
- '.:/app'
- '/app/node_modules'
networks:
- react-frontend
depends_on:
- backend
links:
- backend

backend:
build: backend
restart: always
ports:
- 8000:8000
depends_on:
- mongo
links:
- mongo
environment:
- ASPNETCORE_URLS=http://+:8000
- EnableSwagger=true
networks:
- react-backend
- react-frontend

mongo:
restart: always
image: mongo:4.2.0
volumes:
- ./data:/data/db
networks:
- react-backend

mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
depends_on:
- mongo
links:
- mongo
networks:
- react-backend

networks:
react-backend: {}
react-frontend: {}

The compose file also includes a Mongo-Express server in the Compose file so we can quickly explore the NoSQL documents and check everything end-to-end from UI in React either Swagger document generated for us with .NET

Also, I added a EnableSwagger flag as a Environment variable, which can be read and set accordingly in case you wan to expose Swagger endpoint. To learn more about Appsettings.json, environment variables and overriding them for Docker containers you can follow this link:

Bonus tip: CosmosDB support out of the box!

We can take advantage of using Azure CosmosDB, which supports MongoDB connector so it’s pretty easy to set-up and swap from vanilla MongoDB to use CosmosDB. Here I include some links for reference along with more samples to follow-up:

Happy coding!

Share this post
  

Software Engineer at Microsoft | Former MVP, Certified developer
@stvansolano
More about me

 Browse by tag

 Xamarin

 

 DotNet

 

 Spanish/Español

 

 Productividad

 

 Open Source

 

 Microsoft

 

 AppCenter

 

 DevOps

 

 Speaking

 

 Conferences

 

 Google

 

 Docker

 

 Kubernetes

 

 XamarinDiplomado

 

 ASP.NET

 

 Web Apps

 

 Yeoman

 

 HTML5

 

 REST

 

 Web API

 

 Meetup

 

 Freelance

 

 Android

 

 Mobile

 

 CodeMentor

 

 OSS

 

 containers

 

 Codespaces

 

 Github

 

 React

 

 MVVM

 

 iOS

 

 FSharp

 

 MVP

 

 Comunidad

 

 Azure

 

 AI

 

 Exam

 

 Certification

 

 Data

 

 Rifa

 

 Markdown

 

 GitHub

 

 Backend

 

 Online

 

 WebAPI

 

 MacOS

 

 Asp.Net Core

 

 Taller

 

 Charla

 

 Git

 

 WordPress

 

 Spanish/English

 

 UX / UI

 

 Realm

 

 Bootcamp

 

 Productivity

 

 VisualStudio

 

 Contenedores

 

 M365

 

 MAUI

 

 .NET

 

 Serverless

 

 AspNet

 

 DevDays

 

 Maui

 

 Workshop

 

 XamarinForms

 

 Cloud

 

 NetCore

 

 SignalR

 

 CosmosDB

 

 MachineLearning

 

 Python

 

 MVPSummit

 

 YouTube

 

 VS4Mac

 

 Linux

 

 Windows

 

 Career

 

 OpenSource

 

 TestCloud