Showing posts with label mongodb. Show all posts
Showing posts with label mongodb. Show all posts

Tuesday, November 28, 2023

Using Mongo DB in an ASP.NET 8.0 Minimal Web API web app

 In this tutorial I will show you how to develop an ASP.NET 8.0 Minimal API application that interacts with MongoDB. In order to proceed you will need the following pre-requisites:

  • Docker - used to host MongoDB. This is my preferred approach because it is quick and does not take too many resources on the host computer.
  • .NET 8.0
  • Visual Studio Code

The Database

Run the following command in a terminal window to start a MongoDB container named mdb:

docker run -p 27777:27017 --name mgo -d mongo:4.1.6

You can verify that the container is running by typing the following command in a terminal window:

docker ps -a

Let us start a bash session inside the container so that we can create a database and add some sample data to it. Enter the following command to start an interactive bash session inside the container:

docker exec -it mgo bash

We can then use the mongo command line interface (CLI) to create a database named school-db and then add some sample data to a collection of students. Type the following command:

mongo

This takes you into a mongo session command prompt. Enter the following command to create a database named school-db:

use school-db

Next, let's create a collection named students and add to it six sample students:

db.Students.insertMany(
[
  {'FirstName':'Sue','LastName':'Fox','School':'Business'},
  {'FirstName':'Tom','LastName':'Max','School':'Mining'},
  {'FirstName':'Ann','LastName':'Lee','School':'Nursing'},
  {'FirstName':'Joe','LastName':'Roy','School':'Tourism'},
  {'FirstName':'Jan','LastName':'Ash','School':'Communications'},
  {'FirstName':'Eva','LastName':'Day','School':'Medicine'},
]);

You can retrieve the list of students with the following command:

db.Students.find().pretty();

To exit the mongo command prompt by typing:

exit

You can also exit the bash session and return to the host operating system by typing:

exit

Creating an ASP.NET 8.0 Minimal Web API application

Create an ASP.NET 8.0 Minimal Web API application. This is accomplished by entering this command:

dotnet new webapi -f net8.0 -o AspMongoApi

Let's see what our application looks like. Run the application by executing:

cd AspMongoApi
dotnet run

The above command builds and runs the application in a web server. Point your browser to https://localhost:????/weatherforecast (Where ???? is your port number). This is what you should see:


Stop the web server by hitting CTRL+C on the keyboard.

Building the Students API

Add the MongoDB driver Nuget package with the following command:

dotnet add package MongoDB.Driver

Now that we have the driver, we can proceed with coding our Minimal API. I will use Visual Studio Code because it is operating system agnostic. To load your application workspace into VS Code, simply type in the following command from the root folder of your application:

code .

We need a Student class to represent the schema for student documents in a Students collection in the MongoDB school-db database. Create a Models folder. Inside the Models folder, add a file named Student.cs with the following code:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace AspMongoApi.Models;
public class Student {
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string? Id { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    
    [BsonElement("School")]
    public string? Department { get; set; }
}

In the preceding class, the Id property:
  • Is required for mapping the Common Language Runtime (CLR) object to the MongoDB collection.
  • Is annotated with [BsonId] to designate this property as the document's primary key.
  • Is annotated with [BsonRepresentation(BsonType.ObjectId)] to allow passing the parameter as type string instead of an ObjectId structure. Mongo handles the conversion from string to ObjectId.
The Department property is annotated with the [BsonElement] attribute. The attribute's value of School represents the property name in the MongoDB collection.

Add the following to appsettings.json:

"StudentDbSettings": {
"CollectionName": "Students",
"ConnectionString": "mongodb://localhost:27777",
"DatabaseName": "school-db"
},

The entire contents of appsettings.json will look like this:

{
  "StudentDbSettings": {
    "CollectionName": "Students",
    "ConnectionString": "mongodb://localhost:27777",
    "DatabaseName": "school-db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Add StudentDbSettings.cs to the Models folder with this code:

namespace AspMongoApi.Models;

public class StudentsDbSettings {
    public string ConnectionString { get; set; } = null!;
    public string DatabaseName { get; set; } = null!;
    public string CollectionName { get; set; } = null!;
}

The above StudentDbSettings class is used to store the appsettings.json file's StudentDbSettings property values. The JSON and C# property names are named identically to simplify the mapping process.

Add the following code to Program.cs before "var app = builder.Build();" :

builder.Services.Configure<StudentsDbSettings>(
    builder.Configuration.GetSection("StudentDbSettings"));

In the above code, the configuration instance to which the appsettings.json file's StudentDatabaseSettings section binds is registered in the Dependency Injection (DI) container.

Create a folder named Services and add to it a file named StudentService.cs with the following code:

namespace AspMongoApi.Services;

public class StudentsService {
    private readonly IMongoCollection<Student> _studentsCollection;

    public StudentsService(IOptions<StudentsDbSettings> studentsDatabaseSettings) {
        var mongoClient = new MongoClient(
            studentsDatabaseSettings.Value.ConnectionString);

        var mongoDatabase = mongoClient.GetDatabase(
            studentsDatabaseSettings.Value.DatabaseName);

        _studentsCollection = mongoDatabase.GetCollection<Student>(
            studentsDatabaseSettings.Value.CollectionName);
    }

    public async Task<List<Student>> GetAsync() =>
        await _studentsCollection.Find(_ => true).ToListAsync();

    public async Task<Student?> GetAsync(string id) =>
        await _studentsCollection.Find(x => x.Id == id).FirstOrDefaultAsync();

    public async Task CreateAsync(Student newStudent) =>
        await _studentsCollection.InsertOneAsync(newStudent);

    public async Task UpdateAsync(string id, Student updatedStudent) =>
        await _studentsCollection.ReplaceOneAsync(x => x.Id == id, updatedStudent);

    public async Task RemoveAsync(string id) =>
        await _studentsCollection.DeleteOneAsync(x => x.Id == id);
}

In the above code, an StudentsDbSettings instance is retrieved from Dependency Injection via constructor injection. Also, the above class knows how to connect to the MongoDB server and use the available driver methods to retrieve, insert, update and delete data.

Register the StudentService class with DI to support constructor injection. Add the following to Program.cs before "var app = builder.Build();" :

builder.Services.AddSingleton<StudentsService>();

Add the following code to Program.cs right before the "app.Run()l" statement:

app.MapGet("/students", async (StudentsService studentsService) => {
    var students = await studentsService.GetAsync();
    return students;
});

app.MapGet("/students/{id}", async (StudentsService studentsService, string id) => {
    var student = await studentsService.GetAsync(id);
    return student is null ? Results.NotFound() : Results.Ok(student);
});

app.MapPost("/students", async (StudentsService studentsService, Student student) => {
    await studentsService.CreateAsync(student);
    return student;
});

app.MapPut("/students/{id}", async (StudentsService studentsService, string id, Student student) => {
    await studentsService.UpdateAsync(id, student);
    return student;
});

app.MapDelete("/students/{id}", async (StudentsService studentsService, string id) => {
    await studentsService.RemoveAsync(id);
    return Results.Ok();
});

The above endpoints:
  • Use the StudentService class to perform CRUD operations.
  • Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.

Running the application

Start the application by executing the this command from a terminal windows at the root of the application:

dotnet watch

Test the API with the Swagger interface that is available in your browser:




You can also view the data by pointing your browser to https://localhost:????/api/Students. This is the expected output:


Note that even though the last field is named School in the database, it appears as Department in our app because that is how we mapped it in the Student class.

Go ahead and test the other API endpoints for POST, PUT and DELETE using your favourite tool like Postman or curl. It should all work.

Cleanup

To stop & remove the MongoDB docker container, enter the following from any terminal window on your computer:

docker rm -f mgo

I hope you learned something new in this tutorial. Until next time, happy coding.

Saturday, February 5, 2022

ASP.NET 6.0 Web API and Mongo DB

In this tutorial I will show you how to develop an ASP.NET 6.0 API application that interacts with MongoDB. In order to proceed you will need the following pre-requisites:

  • Docker - used to host MongoDB. This is my preferred approach because it is quick and does not take too many resources on the host computer.
  • .NET 6.0
  • Visual Studio Code

The Database

Run the following command in a terminal window to start a MongoDB container named mdb:

docker run -p 27777:27017 --name mgo -d mongo:4.1.6

You can verify that the container is running by typing the following command in a terminal window:

docker ps -a

Let us start a bash session inside the container so that we can create a database and add some sample data to it. Enter the following command to start an interactive bash session inside the container:

docker exec -it mgo bash

We can then use the mongo command line interface (CLI) to create a database and then add some sample data to a collection of students. Type the following command:

mongo

This takes you into a mongo session command prompt. Enter the following command to create a database named school-db:

use school-db

Next, let's create a collection named students and add to it six sample students:

db.Students.insertMany(
[
  {'FirstName':'Sue','LastName':'Fox','School':'Business'},
  {'FirstName':'Tom','LastName':'Max','School':'Mining'},
  {'FirstName':'Ann','LastName':'Lee','School':'Nursing'},
  {'FirstName':'Joe','LastName':'Roy','School':'Tourism'},
  {'FirstName':'Jan','LastName':'Ash','School':'Communications'},
  {'FirstName':'Eva','LastName':'Day','School':'Medicine'},
]);

You can retrieve the list of students with the following command:

db.Students.find({}).pretty();

To exit the mongo command prompt by typing:

exit

You can also exit the bash session and return to the host operating system by typing:

exit

Creating an ASP.NET 6.0 Web API application

Create an ASP.NET 6.0 Web API application. This is accomplished by entering this command:

dotnet new webapi -f net6.0 -o AspMongoApi

Let us see what our application looks like. Run the application by executing:

cd AspMongoApi
dotnet run

The above command builds and runs the application in a web server. Point your browser to https://localhost:????/weatherforecast (Where ???? is your port number). This is what you should see:


Stop the web server by hitting CTRL+C on the keyboard.

Building the Students API

Find the latest version of MongoDB driver from https://www.nuget.org/packages/MongoDB.Driver/. At the time of writing, the latest version was 2.14.1. Add the MongoDB driver Nuget package with the following command:

dotnet add package MongoDB.Driver --version 2.14.1

Now that we have the driver, we can proceed with coding our API. I will use Visual Studio Code because it is operating system agnostic. To load your application workspace into VS Code, simply type in the following command from the root folder of your application:

code .

We need a Student class to represent the schema for student documents in the Students collection in the MongoDB school-db database. Create a Models folder. Inside the Models folder, add a file named Student.cs with the following code:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace AspMongoApi.Models;
public class Student {
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string? Id { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    
    [BsonElement("School")]
    public string? Department { get; set; }
}

In the preceding class, the Id property:
  • Is required for mapping the Common Language Runtime (CLR) object to the MongoDB collection.
  • Is annotated with [BsonId] to designate this property as the document's primary key.
  • Is annotated with [BsonRepresentation(BsonType.ObjectId)] to allow passing the parameter as type string instead of an ObjectId structure. Mongo handles the conversion from string to ObjectId.
The Department property is annotated with the [BsonElement] attribute. The attribute's value of School represents the property name in the MongoDB collection.
Add the following to appsettings.json:

"StudentDbSettings": {
"CollectionName": "Students",
"ConnectionString": "mongodb://localhost:27777",
"DatabaseName": "school-db"
},

The entire contents of appsettings.json will look like this:

{
  "StudentDbSettings": {
    "CollectionName": "Students",
    "ConnectionString": "mongodb://localhost:27777",
    "DatabaseName": "school-db"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Add StudentDbSettings.cs to the Models folder with this code:

namespace AspMongoApi.Models;

public class StudentsDbSettings {
    public string ConnectionString { get; set; } = null!;
    public string DatabaseName { get; set; } = null!;
    public string CollectionName { get; set; } = null!;
}

The above StudentDbSettings class is used to store the appsettings.json file's StudentDbSettings property values. The JSON and C# property names are named identically to simplify the mapping process.

Add the following code to Program.cs before "var app = builder.Build();" :

builder.Services.Configure<StudentsDbSettings>(
    builder.Configuration.GetSection("StudentDbSettings"));

In the above code, the configuration instance to which the appsettings.json file's StudentDatabaseSettings section binds is registered in the Dependency Injection (DI) container.

Create a folder named Services and add to it a file named StudentService.cs with the following code:

namespace AspMongoApi.Services;

public class StudentsService {
    private readonly IMongoCollection<Student> _studentsCollection;

    public StudentsService(IOptions<StudentsDbSettings> studentsDatabaseSettings) {
        var mongoClient = new MongoClient(
            studentsDatabaseSettings.Value.ConnectionString);

        var mongoDatabase = mongoClient.GetDatabase(
            studentsDatabaseSettings.Value.DatabaseName);

        _studentsCollection = mongoDatabase.GetCollection<Student>(
            studentsDatabaseSettings.Value.CollectionName);
    }

    public async Task<List<Student>> GetAsync() =>
        await _studentsCollection.Find(_ => true).ToListAsync();

    public async Task<Student?> GetAsync(string id) =>
        await _studentsCollection.Find(x => x.Id == id).FirstOrDefaultAsync();

    public async Task CreateAsync(Student newStudent) =>
        await _studentsCollection.InsertOneAsync(newStudent);

    public async Task UpdateAsync(string id, Student updatedStudent) =>
        await _studentsCollection.ReplaceOneAsync(x => x.Id == id, updatedStudent);

    public async Task RemoveAsync(string id) =>
        await _studentsCollection.DeleteOneAsync(x => x.Id == id);
}

In the above code, an StudentsDbSettings instance is retrieved from DI via constructor injection. Also, the above class knows how to connect to the MongoDB server and use the available driver methods to retrieve, insert, update and delete data.

To register the StudentService class with DI to support constructor injection, add the following to Program.cs before "var app = builder.Build();" :

builder.Services.AddSingleton<StudentsService>();

Add a StudentsController.cs class to the Controllers folder with the following code:

namespace AspMongoApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class StudentsController : ControllerBase {
    private readonly StudentsService _studentsService;

    public StudentsController(StudentsService studentsService) =>
        _studentsService = studentsService;

    [HttpGet]
    public async Task<List<Student>> Get() =>
        await _studentsService.GetAsync();

    [HttpGet("{id:length(24)}")]
    public async Task<ActionResult<Student>> Get(string id) {
        var student = await _studentsService.GetAsync(id);

        if (student is null) {
            return NotFound();
        }

        return student;
    }

    [HttpPost]
    public async Task<IActionResult> Post(Student newStudent) {
        await _studentsService.CreateAsync(newStudent);

        return CreatedAtAction(nameof(Get), new { id = newStudent.Id }, newStudent);
    }

    [HttpPut("{id:length(24)}")]
    public async Task<IActionResult> Update(string id, Student updatedStudent) {
        var student = await _studentsService.GetAsync(id);

        if (student is null) {
            return NotFound();
        }

        updatedStudent.Id = student.Id;

        await _studentsService.UpdateAsync(id, updatedStudent);

        return NoContent();
    }

    [HttpDelete("{id:length(24)}")]
    public async Task<IActionResult> Delete(string id) {
        var student = await _studentsService.GetAsync(id);

        if (student is null) {
            return NotFound();
        }

        await _studentsService.RemoveAsync(student.Id!);

        return NoContent();
    }
}

The StudentsController class:
  • Uses the StudentService class to perform CRUD operations.
  • Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.

Running the application

Start the application by executing the this command from a terminal windows at the root of the application:

dotnet run

You can view the data by pointing your browser to https://localhost:????/api/Students. This is the expected output:


Note that even though the last field is named School in the database, it appears as Department in our app because that is how we mapped it in the Student class.

Go ahead and test the other API endpoints for POST, PUT and DELETE using your favourite tool like Postman or curl. It should all work.

Cleanup

To stop & remove the MongoDB docker container, enter the following from any terminal window on your computer:

docker rm -f mgo

I hope you learned something new in this tutorial. Until next time, happy coding.

Reference:https://referbruv.com/blog/posts/integrating-mongodb-with-aspnet-core-(net-6)

Friday, August 9, 2019

ASP.NET Core 2.2 Web API application that works with Mongo database

You can do this tutorial on Linux, Mac or Windows.

In this tutorial I will show you how to develop an ASP.NET 2.2 API application that interacts with MongoDB. In order to proceed you will need the following pre-requisites:
  • Docker - used to host MongoDB. This is my preferred approach because it is quick and does not take too many resources on the computer.
  • .NET Core 2.2
  • Visual Studio Code
Source code for this tutorial can be found at https://github.com/medhatelmasry/AspMongo.
Video: https://youtu.be/TgD4qYrvBaY

The Database

Run the following command in a terminal window to start a MongoDB container named mdb:

docker run -p 22222:27017 --name mdb -d mongo:4.0.11

You can verify that the container is running by typing the following command in a terminal window:

docker ps -a

Let us start a bash session inside the container so that we can create a database and add some sample data to it. Enter the following command to start an interactive bash session inside the container:

docker exec -it mdb bash

We can then use the mongo command line interface (CLI) to create a database and then add some sample data to a collection of students. Type the following command:

mongo

This takes you into a mongo session command prompt. Enter the following command to create a database named CollegeDB:

use CollegeDB

Next, let's create a collection named students and add to it six sample students:

db.Students.insertMany(
[
  {'FirstName':'Sue','LastName':'Fox','School':'Business'},
  {'FirstName':'Tom','LastName':'Max','School':'Mining'},
  {'FirstName':'Ann','LastName':'Lee','School':'Nursing'},
  {'FirstName':'Joe','LastName':'Roy','School':'Tourism'},
  {'FirstName':'Jan','LastName':'Ash','School':'Communications'},
  {'FirstName':'Eva','LastName':'Day','School':'Medicine'},
]);

You can retrieve the list of students with the following command:

db.Students.find({}).pretty();

To exit the mongo command prompt by typing:

CTRL+C

You can also exit the bash session and return to the host operating system by typing:

exit

Creating an ASP.NET 2.2 MVC application

Find out what versions of .NET Core SDKs you have on your computer by running this command in a terminal windows:

dotnet --list-sdks

You will see a list like this:

2.1.504 [C:\Program Files\dotnet\sdk]
2.1.505 [C:\Program Files\dotnet\sdk]
2.1.602 [C:\Program Files\dotnet\sdk]
2.1.700 [C:\Program Files\dotnet\sdk]
2.1.800-preview-009677 [C:\Program Files\dotnet\sdk]
2.2.104 [C:\Program Files\dotnet\sdk]
3.0.100-preview6-012264 [C:\Program Files\dotnet\sdk]

I chose to use the exact version 2.2.104.

Choose a suitable workspace on your computer then create a directory named AspMongo. While in a terminal window inside of the AspMongo directory, add a global.json file that targets ASP.NET Core 2.2 with the following command:

dotnet new globaljson --sdk-version 2.2.104 

The above command generated a global.json file that looks like this:
{
 "sdk": {
    "version": "2.2.104"
  }
}
The next step is to create an ASP.NET 2.2 MVC template application. This is accomplished by entering this command:

dotnet new mvc

Let us see what our application looks like. Run the application by executing:

dotnet run

The above command builds and runs the application in a web server. Point your browser to https://localhosy:5001. This is what you should see:


Stop the web server by hitting CTRL+C on the keyboard.

Building the Students API

Find the latest version of MongoDB driver from https://www.nuget.org/packages/MongoDB.Driver/. At the time of writing, the latest version was 2.8.1. Add the MongoDB driver Nuget package with the following command:

dotnet add package MongoDB.Driver --version 2.8.1

Now that we have the driver, we can proceed with coding our API. I will use Visual Studio Code because it is operating system agnostic. To load your application workspace into VS Code, simply type in the following command from the root folder of your application:

code .

We need a Student class to represent the schema for student documents in the Students collection in MongoDB. Inside the Models folder, add a file named Student.cs with the following code:

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace AspMongo.Models {
  public class Student {
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string StudentId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    [BsonElement("School")]
    public string Department { get; set; }
  }
}

In the preceding class, the StudentId property:
  • Is required for mapping the Common Language Runtime (CLR) object to the MongoDB collection.
  • Is annotated with [BsonId] to designate this property as the document's primary key.
  • Is annotated with [BsonRepresentation(BsonType.ObjectId)] to allow passing the parameter as type string instead of an ObjectId structure. Mongo handles the conversion from string to ObjectId.
The Department property is annotated with the [BsonElement] attribute. The attribute's value of School represents the property name in the MongoDB collection.
Add the following to appsettings.json:

"StudentDatabaseSettings": {
  "StudentsCollectionName": "Students",
  "ConnectionString": "mongodb://localhost:22222",
  "DatabaseName": "CollegeDB"
},

The entire contents of appsettings.json will look like this:

{
  "StudentDatabaseSettings": {
    "StudentsCollectionName": "Students",
    "ConnectionString": "mongodb://localhost:22222",
    "DatabaseName": "CollegeDB"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Add StudentDatabaseSettings.cs to the Models folder with this code:

namespace AspMongo.Models {
  public class StudentDatabaseSettings : IStudentDatabaseSettings {
      public string StudentsCollectionName { get; set; }
      public string ConnectionString { get; set; }
      public string DatabaseName { get; set; }
  }
  public interface IStudentDatabaseSettings {
      string StudentsCollectionName { get; set; }
      string ConnectionString { get; set; }
      string DatabaseName { get; set; }
  }   
}

The above StudentDatabaseSettings class is used to store the appsettings.json file's StudentDatabaseSettings property values. The JSON and C# property names are named identically to ease the mapping process.

Add the following code to Startup.ConfigureServices():

services.Configure<StudentDatabaseSettings>(
  Configuration.GetSection(nameof(StudentDatabaseSettings)));

services.AddSingleton<IStudentDatabaseSettings>(sp =>
  sp.GetRequiredService<IOptions<StudentDatabaseSettings>>().Value);
In the above code:
  • The configuration instance to which the appsettings.json file's StudentDatabaseSettings section binds is registered in the Dependency Injection (DI) container.
  • The IStudentDatabaseSettings interface is registered in DI with a singleton service lifetime. When injected, the interface instance resolves to a StudentDatabaseSettings object.
Create a folder named Services and add to it a file named Studentservice.cs with the following code:

using System.Collections.Generic;
using AspMongo.Models;
using MongoDB.Driver;
namespace AspMongo.Services {
  public class StudentService {
    private readonly IMongoCollection<Student> _Students;
    public StudentService(IStudentDatabaseSettings settings) {
      var client = new MongoClient(settings.ConnectionString);
      var database = client.GetDatabase(settings.DatabaseName);
      _Students = database.GetCollection<Student>(settings.StudentsCollectionName);
    }
    public List<Student> Get() =>
      _Students.Find(student => true).ToList();
    public Student Get(string id) =>
      _Students.Find<Student>(student => student.StudentId == id).FirstOrDefault();
    public Student Create(Student student) {
      _Students.InsertOne(student);
      return student;
    }
    public void Update(string id, Student studentIn) =>
      _Students.ReplaceOne(student => student.StudentId == id, studentIn);
    public void Remove(Student studentIn) =>
      _Students.DeleteOne(student => student.StudentId == studentIn.StudentId);
    public void Remove(string id) =>
      _Students.DeleteOne(student => student.StudentId == id);
  }
}

In the above code, an IStudentDatabaseSettings instance is retrieved from DI via constructor injection. Also, the above class knows how to connect to the MongoDB server and use the available driver methods to retrieve, insert, update and delete data.

To register the StudentService class with DI to support constructor injection, add the following to Startup.ConfigureServices():

services.AddSingleton<StudentService>();

Add a StudentsController.cs class to the Controllers directory with the following code:

using System.Collections.Generic;
using AspMongo.Models;
using AspMongo.Services;
using Microsoft.AspNetCore.Mvc;
namespace AspMongo.Controllers {
  [Route("api/[controller]")]
  [ApiController]
  public class StudentsController : ControllerBase {
      private readonly StudentService _StudentService;
      public StudentsController(StudentService studentService) {
          _StudentService = studentService;
      }
      [HttpGet]
      public ActionResult<List<Student>> Get() =>
          _StudentService.Get();
      [HttpGet("{id:length(24)}", Name = "GetStudent")]
      public ActionResult<Student> Get(string id) {
          var student = _StudentService.Get(id);
          if (student == null)
              return NotFound();
          return student;
      }
      [HttpPost]
      public ActionResult<Student> Create(Student student) {
          _StudentService.Create(student);
          return CreatedAtRoute("GetStudent", new { id = student.StudentId.ToString() }, student);
      }
      [HttpPut("{id:length(24)}")]
      public IActionResult Update(string id, Student studentIn) {
          var student = _StudentService.Get(id);
          if (student == null)
              return NotFound();
          _StudentService.Update(id, studentIn);
          return NoContent();
      }
      [HttpDelete("{id:length(24)}")]
      public IActionResult Delete(string id) {
          var student = _StudentService.Get(id);
          if (student == null)
              return NotFound();
          _StudentService.Remove(student.StudentId);
          return NoContent();
      }
  }
}

The StudentsController class:
  • Uses the StudentService class to perform CRUD operations.
  • Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.
  • Calls CreatedAtRoute in the Create action method to return an HTTP 201 response.

Running the application

Start the application by executing the this command from a terminal windows at the root of the application:

dotnet run

You can view the data by pointing your browser to https://localhost:5001/api/Students. This is the expected output:

Go ahead and test the other API endpoints for POST, PUT and DELETE using your favorite tool like Postman or curl. It should all work.

I hope you learned something new in this tutorial, Until next time, happy coding.

Saturday, January 26, 2019

Node Express with TypeScript

Introduction
This walkthrough is intended to help you convert a standard Node Express application so that it uses TypeScript instead.

Preparation
Install TypeScript globally:

npm install typescript -g

Let us create a directory simply to play around and understand how TypeScript works:

mkdir tsprimer
cd tsprimer
code .

Create a file named greeter.ts and add to it the following code:

function greeter(person) {
    return "Hello, " + person;
}
let user = "Jane Bond";
console.log(greeter(user));

To trandspile the app, type:

tsc greeter.ts

This transpiles the app to greeter.js, which gets created in the same directory. Run the application with:

node greeter.js

Interfaces
Let us modify greeter.ts so that it uses strong type checking. Create a file named person.ts and add to it the following Person interface:

export interface Person {
    firstName: string;
    lastName: string;
}

Add a file named greeterPerson.ts that uses the Person interface with the following code:

import {Person} from './person';
function greeter(person: Person) : string{
    return "Hello, " + person.firstName + " " + person.lastName;
}
let person : Person = { firstName: "Jane", lastName: "Bond" };
console.log(greeter(person));

Transpile the app with tsc greeterPerson.ts and run it with:

node greeterPerson.js

Classes
Create a file named student.ts to hold a Student class. The code is:

export class Student {
    fullName: string;
    constructor(public firstName: string, public middleInitial: string, public lastName: string) {
        this.fullName = firstName + " " + middleInitial + " " + lastName;
    }
    public getFullName = () : string => {
        return this.fullName;
    };
}

Notice the instance variable fullName, constructor and method getFullName(). Create a greeter file named greeterStudent.ts that uses the Student class as shown below:

import {Student} from './student';
let student  = new Student("Jane","M.","Bond" );
console.log(student.getFullName());

Transpile & run the application.

TS with Node Express
Go to the parent directory with:

cd ..

create a new directory named tsexpress and go into that directory:

mkdir tsexpress
cd tsexpress

Let us initialize a node project in the current directory with:

npm init

Let’s now install the typescript package locally with:

npm install typescript --save-dev

Open the current directory in Visual Studio Code.

Inside our package.json, add a script called tsc:

"scripts": {
    "tsc": "tsc"
},

This modification allows us to call TypeScript functions from the command line in the project’s folder.

Run the following command to initialize the typescript project by creating the tsconfig.json file.

npm run tsc -- --init

Edit tsconfig.json, uncomment outDir setting so that it looks like this:

"outDir": "./build",

Also, make sure you have these additional settings in tsconfig.json:

    "target": "es5", 
    "module": "commonjs",
    "sourceMap": true, 
    "strict": true,
    "pretty": true,
    "moduleResolution": "node",
    "baseUrl": "./app",
    "esModuleInterop": true

Add this to the bottom of tsconfig.json:

  "include": [
    "app/**/*.ts"
  ],
  "exclude": [
      "node_modules"
  ]

Installing express.js

Run the following command:

npm install express --save

Express and Typescript packages are independent. The consequence of this is that Typescript does not “know” types of Express classes. Run this specific npm package for the Typescript to recognize the Express class types.

npm install @types/express --save

Let us add a very simple Hello World Node Express application file. Create a folder named app and inside the app directory create a file named app.ts with this content:

// app/app.ts
import express = require('express');

// Create a new express application instance
const app: express.Application = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Example app listening on port 3000!');
});

To transpile your first TypeScript Node Express app, run the following command in the root directory of your application:

npm run tsc

This command automatically created the build folder and adds to it the transpiled app.js file.

 

 To run the application in build/app.js, type:

node build/app.js

Point your browser to http://localhost:3000. You should see the following:


Running TypeScript without transpiling
You can run TypeScript code directly with the ts-node package.

This package is recommended for development purposes only. To make the final deployment to production, always use the JavaScript version of your project.

The ts-node is already included as a dependency on another package, ts-node-dev. After installing ts-node-dev we can run commands that restarts the server whenever a project file changes. Install  ts-node-dev as follows:

npm install ts-node-dev --save-dev

Add the following two script lines into the package.json file:

"dev": "ts-node-dev --respawn --transpileOnly ./app/app.ts",
"prod": "tsc && node ./build/app.js"

To start the development environment:

npm run dev

 To run the server in production mode:

npm run prod

Pure TypeScript Classes
So far in our Node/Express sample application, we are not really using proper TypeScript syntax. Let’s start making use of the powerful TypeScript platform.

Create a file named server.ts in the app folder and add to it the following code:

// app/server.ts
import app from "./app";
const PORT = 3000;

app.listen(PORT, () => {
    console.log('Express server listening on port ' + PORT);
})

In package.json:

change app.js                 TO              server.js
change app.ts                 TO             server.ts

Create a folder named routes under app and add to it the following index.ts file:

// /app/routes/index.ts
import {Request, Response} from "express";
export class Routes { 
    public routes(app: any): void { 
        app.route('/')
            .get((req: Request, res: Response) => {            
                res.status(200).send('Hello World!');
        });    
    }
}

Replace code in app/app.ts with following code:

// app/app.ts
import express from "express";
import bodyParser from "body-parser";
import { Routes } from "./routes";

class App {
    public app: express.Application;
    public routePrv: Routes = new Routes();

    constructor() {
        this.app = express();
        this.config(); 
        this.routePrv.routes(this.app); 
    }

    private config(): void { 
        this.app.use(function(req, res, next) {
            res.header("Access-Control-Allow-Origin", "*");         
            res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");         
            res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
            next();
        });           

        // support application/json type post data
        this.app.use(bodyParser.json());
        //support application/x-www-form-urlencoded post data
        this.app.use(bodyParser.urlencoded({ extended: false }));
    }
}
export default new App().app;

Test the application in dev mode by running:

npm run dev 

Then point your browser to http://localhost:3000. You should see Hello World in the browser.

Test same as above in production mode by running:

npm run prod

Using mongoose

Mongoose is an Object Data Modeling (ODM) library for MongoDB and Node.js. In this section we will talk get our application to conntect with a MopngoDB database using the Mongoose driver. It is assumed that you have a MongoDB database running on your development computer and listening on port 27017.

Install these mongoose packages:

npm install mongoose –save
npm install @types/mongoose --save

Create a folder named models under app and add to it a file named student.ts with the following code:

//   /app/models/student.ts
import mongoose from 'mongoose'; 
const Schema = mongoose.Schema;
// create a schema
export const StudentSchema = new Schema({
    FirstName: {
        type: String,
        required: true
    },
    LastName: {
        type: String,
        required: true
    },
    School: {
        type: String,
        required: true
    },
    StartDate: {
        type: Date,
        required: true
    }
});

Create a folder called controllers under app and add to it a file named studentController.ts with this code:

//   /app/controllers/studentController.ts
import * as mongoose from 'mongoose';
import { StudentSchema } from '../models/student';
import { Request, Response } from 'express';

const StudentMongooseModel = mongoose.model('Student', StudentSchema);

export class StudentController { 

    public addNewStudent (req: Request, res: Response) {                
        let newStudent = new StudentMongooseModel(req.body);

        /*
        let newStudent = new StudentMongooseModel({
            "FirstName": "Tom",
            "LastName": "Baker",
            "School": "45 River Road",
            "StartDate": "2001-11-17T19:23:15.118Z"
        });
        */
    
       newStudent.save((err, data) => {
            if (err){
                res.send(err);
            }    
            res.json(data);
        });
    }

    public getStudents (req: Request, res: Response) {           
        StudentMongooseModel.find({}, (err, data) => {
            if (err){
                res.send(err);
            }
            res.json(data);
        });
    }

    public getStudentById (req: Request, res: Response) {           
        StudentMongooseModel.findById(req.params.studentId, (err, data) => {
            if (err){
                res.send(err);
            }
            res.json(data);
        });
    }

    public updateStudent (req: Request, res: Response) {           
        StudentMongooseModel.findOneAndUpdate({ _id: req.params.studentId }, req.body, { new: true }, 
            (err, data) => {
            if (err){
                res.send(err);
            }
            res.json(data);
        });
    }

    public deleteStudent (req: Request, res: Response) {           
        StudentMongooseModel.findOneAndRemove({ _id: req.params.studentId }, (err, data) => {
            if (err){
                res.send(err);
            }
            res.json({ message: 'Successfully deleted student!'});
        });
    }

    public generateDummyData (req: Request, res: Response) {     
        var data = [
            {
            "FirstName":"Sally",
            "LastName":"Baker",
            "School":"Mining",
            "StartDate": new Date("2012-02-20T08:30:00")
            },{
            "FirstName":"Jason",
            "LastName":"Plumber",
            "School":"Engineering",
            "StartDate": new Date("2018-03-17T17:32:00")
            },{
            "FirstName":"Sue",
            "LastName":"Gardner",
            "School":"Political Science",
            "StartDate": new Date("2014-06-20T08:30:00")
        },{
            "FirstName":"Linda",
            "LastName":"Farmer",
            "School":"Agriculture",
            "StartDate": new Date("2014-06-20T08:30:00")
            },{
            "FirstName":"Fred",
            "LastName":"Fisher",
            "School":"Environmental Sciences",
            "StartDate": new Date("2017-10-16T17:32:00")
            }
        ];
          
        StudentMongooseModel.collection.insert(data, function (err, docs) { 
            if (err){
                res.send(err);
            }
            res.json({ message: 'Successfully generated 5 sample documents!'});
        });
    }
}

Add this instance variable to the Routes class in routes/index.ts and resolve it by importing the appropriate module:

studentController: StudentController = new StudentController();

Add these routes to routes/index.ts:

        // Get all students
        app.route('/api/students')
            .get(this.studentController.getStudents);

        // Create a new student
        app.route('/api/students')
            .post(this.studentController.addNewStudent);

        // get a specific student
        app.route('/api/students/:studentId')
            .get(this.studentController.getStudentById);

        // update a specific student
        app.route('/api/students/:studentId')
            .put(this.studentController.updateStudent);
        
        // delete a specific student
        app.route('/api/students/:studentId')
            .delete(this.studentController.deleteStudent);
        
        // generate dummy data
        app.route('/api/dummy')
            .get(this.studentController.generateDummyData);

Notice that the last route, /api/dummy, which is for generating dummy data.
Back in /app/app.ts, import mongoose with:

import mongoose from "mongoose";

Add this method to /app/app.ts:

    private mongoSetup(): void{
        mongoose.connect('mongodb://localhost:27017/school', {})
        .then(() => console.log('connection successful'))
        .catch((err) => console.error(err));
    }

In the constructor of /app/app.ts, call the above method with:

this.mongoSetup();

Start the server with:

npm run dev

In your browser, point to http://localhost:3000. You should see Hello World.

Next, point your browser to http://localhost:3000/api/dummy. This should create sample data.

Use URL http://localhost:3000/api/students to see the sample data that was jiust created.