diff --git a/4-restful-http-methods-slides.html b/4-restful-http-methods-slides.html index baf7cf0..0f545ba 100644 --- a/4-restful-http-methods-slides.html +++ b/4-restful-http-methods-slides.html @@ -13,10 +13,10 @@ /* buutti.css */ /* @theme buutti */div#\:\$p>svg>foreignObject>section .columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns12{display:grid;grid-template-columns:1fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns21{display:grid;grid-template-columns:2fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns32{display:grid;grid-template-columns:3fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns23{display:grid;grid-template-columns:2fr 3fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns111{display:grid;grid-template-columns:1fr 1fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .centered{display:flex;flex-direction:column;justify-content:center;text-align:center}div#\:\$p>svg>foreignObject>section .tableborderless td,div#\:\$p>svg>foreignObject>section th{border:none!important;border-collapse:collapse}div#\:\$p>svg>foreignObject>section.extra{background-color:#5d275d;background-image:linear-gradient(to bottom,#401a40,#1d0c1d);color:white}div#\:\$p>svg>foreignObject>section.extra a{color:rgb(145,255,209)}div#\:\$p>svg>foreignObject>section.exercise{background-color:#29366f;background-image:linear-gradient(to bottom,#20636a,#173742);color:white}div#\:\$p>svg>foreignObject>section.exercise a{color:rgb(211,173,255)} -/* @theme 3wqvr8lbfisg883hr04eltgujpfjziivmd8b5evcey9 */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}
+/* @theme lfqpvq1z79ouia9p3ld9aaj4qswwp6p9dixcybwcjc5 */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}

RESTful HTTP Methods

-
+

Contents

-
+

HTTP methods for RESTful APIs

-
+

RESTful API

-
+

Primary HTTP request methods

The primary HTTP request methods with descriptions:

@@ -92,10 +92,10 @@

https://www.restapitutorial.com/lessons/httpmethods.html

-
+

HTTP POST method

-
+

Handling HttpPost Requests

  • Remember that HTTP requests can include a content body
  • @@ -111,7 +111,7 @@
  • However, nothing prevents you from using a custom type variable
-
+
  • ASP.NET deserializes the request content body into an object:
    // Models/Contact.cs
     public class Contact
    @@ -134,10 +134,10 @@
     
-
+

Creating a POST Request with Postman

-
+

Request headers

  • ASP.NET knows to deserialize the content if the content type is set to JSON in the HTTP requests headers @@ -152,7 +152,7 @@

-
+

Request body

After setting the header,

    @@ -165,7 +165,7 @@

-
+

Sending the request

  • If the POST method is routed at http://localhost:54106/api/contacts:
  • @@ -174,7 +174,7 @@

-
+

Exercise 1: Creating a POST Endpoint

Continue working on the CourseAPI.

@@ -183,7 +183,7 @@
-
+

HTTP PUT method

  • Use PUT to replace an existing resource, e.g. an element in a list, with a new one
  • @@ -194,7 +194,7 @@

-
+

Handling HttpPut requests

  • The ID is fetched from the URI and the contents from the request body
  • @@ -211,7 +211,7 @@
-
+

Exercise 2: Creating a PUT Endpoint

  • In CoursesController class, create a method for PUT requests with the URI api/courses/{id} @@ -225,7 +225,7 @@
  • Test with Swagger/Postman
-
+

HTTP DELETE method

  • Use DELETE to delete an existing resource, e.g. an element in a list
  • @@ -234,7 +234,7 @@
-
+

Handling HttpDelete Requests

  • The ID is fetched from the URI
    // Controllers/ContactsController.cs
    @@ -250,7 +250,7 @@
     
-
+

Exercise 3: Creating a DELETE Endpoint

Continue working on CourseAPI.

@@ -263,7 +263,7 @@ d) Return a 404 status code if a course with the corresponding ID d
  • Test with Postman.
  • -
    +

    HTTP PATCH method

    • Use PATCH to partially update a resource @@ -275,7 +275,7 @@ d) Return a 404 status code if a course with the corresponding ID d
    • Sending and handling PATCH requests with ASP.NET requires some extra work and the use of a third party package JSON Patch
    -
    +
    • To handle PATCH requests in a standardized way, install and add the following NuGet packages to your project:
        @@ -291,7 +291,7 @@ d) Return a 404 status code if a course with the corresponding ID d
    -
    +
    • The body of the PATCH request needs to be in the following form:
      [{ "op": "replace", "path": "/propertyName", "value": "newValue"}]
       
      @@ -302,7 +302,7 @@ d) Return a 404 status code if a course with the corresponding ID d

    -
    +

    Handling HttpPatch Requests

    • The ID is fetched from the URI
    • @@ -323,7 +323,7 @@ d) Return a 404 status code if a course with the corresponding ID d
    -
    +

    Exercise 4: Creating a PATCH Endpoint

    Continue working on CourseAPI.

    diff --git a/4-restful-http-methods.md b/4-restful-http-methods.md index de2275a..2353b9e 100644 --- a/4-restful-http-methods.md +++ b/4-restful-http-methods.md @@ -24,8 +24,8 @@ title: 4. RESTful HTTP Methods ### RESTful API * We'll be extending our Web API into a full-blown ***RESTful API*** - * More about REST in [Frontend Basics Lecture 3: REST Architecture](https://gitea.buutti.com/education/frontend-basics/src/branch/main/3-rest-architecture.md) - * A good idea to also check out [Frontend Basics Lecture 2: HTTP methods](https://gitea.buutti.com/education/frontend-basics/src/branch/main/2-http.md) + * It's a good idea to check out [Webdev Basics: HTTP methods](https://gitea.buutti.com/education/webdev-basics/src/branch/main/http.md) first + * Also, some ramblings about REST in [Webdev Basics: REST Architecture](https://gitea.buutti.com/education/webdev-basics/src/branch/main/rest-architecture.md) * We have so far implemented `GET` and `POST` methods for reading resources from the API and creating new ones to it, respectively * All the primary methods for following the uniform interface requirement are `GET`, `POST`, `PUT`, `PATCH` and `DELETE` * Others exist, but these are by far most commonly used diff --git a/6-databases-with-entity-framework-slides.html b/6-databases-with-entity-framework-slides.html index 02a2a14..5cb70ac 100644 --- a/6-databases-with-entity-framework-slides.html +++ b/6-databases-with-entity-framework-slides.html @@ -13,30 +13,36 @@ /* buutti.css */ /* @theme buutti */div#\:\$p>svg>foreignObject>section .columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns12{display:grid;grid-template-columns:1fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns21{display:grid;grid-template-columns:2fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns32{display:grid;grid-template-columns:3fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns23{display:grid;grid-template-columns:2fr 3fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns111{display:grid;grid-template-columns:1fr 1fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .centered{display:flex;flex-direction:column;justify-content:center;text-align:center}div#\:\$p>svg>foreignObject>section .tableborderless td,div#\:\$p>svg>foreignObject>section th{border:none!important;border-collapse:collapse}div#\:\$p>svg>foreignObject>section.extra{background-color:#5d275d;background-image:linear-gradient(to bottom,#401a40,#1d0c1d);color:white}div#\:\$p>svg>foreignObject>section.extra a{color:rgb(145,255,209)}div#\:\$p>svg>foreignObject>section.exercise{background-color:#29366f;background-image:linear-gradient(to bottom,#20636a,#173742);color:white}div#\:\$p>svg>foreignObject>section.exercise a{color:rgb(211,173,255)} -/* @theme gqy7k3kdx5d6t13nmmhm7jdgeg2yxye7r5enweiq2sgh */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}
    +/* @theme qxwzalhzytjx5wnlf6losqvbl44tf0bt7if1oe7ch9 */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}

    Databases with Entity Framework

    -
    +

    Contents

    -
    +

    Entity Framework (EF)

      -
    • Entity Framework is an Object-Relational Mapper (ORM) made by Microsoft for the .NET framework +
    • This lecture will assume you have a basic understanding of SQL databases + +
    • +
    • Entity Framework is an Object-Relational Mapper (ORM) made by Microsoft for the .NET framework
        -
      • Object-Relational Mapping: converting from database representation to objects in a programming language
      • +
      • Object-Relational Mapping: converting from database representation to objects in a programming language
    • -
    • Allows creation of CRUD operations without writing SQL
    • +
    • Allows creation of CRUD operations without writing SQL
    -
    +

    Entity Framework Core (EF Core)

    • EF Core is a cross-platform version of EF
    • @@ -46,7 +52,7 @@
    • This is what we'll be using
    -
    +

    Code First vs Database First vs Model First

    • There are three approaches through which Entity Framework can be implemented @@ -59,7 +65,7 @@
    • Database First and Code First are the most used ones and will be introduced in this lecture
    -
    +

    Note about loading data

    • In EF Core, you can use navigation properties in your model to load related entities
    • @@ -72,10 +78,10 @@
    -
    +

    Code First approach

    -
    +

    Code First

    • In the Code First approach, Entity Framework will create databases and tables based on defined entity classes
    • @@ -95,7 +101,7 @@
    • Not preferred for data intensive applications
    -
    +

    Required Packages

    • Install and add the following packages to your project: @@ -110,7 +116,7 @@

    -
    +

    Code First: DbContext

    • Let's begin with the Code First Approach
    • @@ -125,7 +131,7 @@ b) classes to keep the database up-to-date with CRUD operations
    -
    +

    Creating a context

    • Create a context that inherits from DbContext @@ -145,7 +151,7 @@ b) classes to keep the database up-to-date with CRUD operations
    -
    +
    • To further configure how the database will be structured, override the OnModelCreating method
    • In this example, one table named Contact with columns Id, Name and Email will be created:
      public class ContactsContext : DbContext
      @@ -162,7 +168,7 @@ b) classes to keep the database up-to-date with CRUD operations
       
    -
    +
    • In this example, the Contact table will be created with some starting values for Id, Name and Email columns:
      public class ContactsContext : DbContext
       {
      @@ -181,7 +187,7 @@ b) classes to keep the database up-to-date with CRUD operations
       
    -
    +

    DbContext as a Service

    • In Program.cs, add the context to services with AddDbContext method
    • @@ -198,7 +204,7 @@ services.AddControllers().AddNewtonsoftJson();
    -
    +

    Migrations

    • As the development progresses, models and database schemas change over time @@ -214,7 +220,7 @@ services.AddControllers().AddNewtonsoftJson();
    • EFCore migrations have built-in version control; a snapshot of each version of the schema is stored
    -
    +

    Applying migrations

    • Open the Package Manager Console in Visual Studio @@ -235,7 +241,7 @@ services.AddControllers().AddNewtonsoftJson();
    -
    +
    • At this point, the values you have entered (Contacts table in this example) should show up in the database. You can check it up e.g. in pgAdmin.
    @@ -256,7 +262,7 @@ services.AddControllers().AddNewtonsoftJson();
    -
    +

    Exercise 1: Adding Context

    Continue working on the CourseAPI.

    @@ -269,7 +275,7 @@ services.AddControllers().AddNewtonsoftJson();
  • Check that the Course table with the starting values has appeared to the database
  • -
    +

    Using DbContext in the API

    • Because DbContext is added to services, it can be accessed from any other service, such as the repository
    • @@ -283,7 +289,7 @@ services.AddControllers().AddNewtonsoftJson();
    • After modifying the DbSet, update the changes to the database with the DbContext.SaveChanges() method
    -
    +

    Injecting DbContext

    • Inject the DbContext to your repositories as you would any other service:
      public class ContactRepository : IContactRepository
      @@ -300,7 +306,7 @@ services.AddControllers().AddNewtonsoftJson();
       
    -
    +

    DbContext: Read Operations

    public class ContactRepository : IContactRepository
     {
    @@ -315,7 +321,7 @@ services.AddControllers().AddNewtonsoftJson();
     }
     
    -
    +

    DbContext: Create Operations

    public class ContactRepository : IContactRepository
     {
    @@ -333,7 +339,7 @@ services.AddControllers().AddNewtonsoftJson();
     }
     
    -
    +

    DbContext: Update Operations

    public class ContactRepository : IContactRepository
     {
    @@ -354,7 +360,7 @@ services.AddControllers().AddNewtonsoftJson();
     }
     
    -
    +

    DbContext: Delete Operations

    public class ContactRepository : IContactRepository
     {
    @@ -372,7 +378,7 @@ services.AddControllers().AddNewtonsoftJson();
     }
     
    -
    +

    Exercise 2: CRUD on the DB

    Continue working on CourseAPI.

    @@ -381,7 +387,7 @@ services.AddControllers().AddNewtonsoftJson();
  • Test with Postman. Keep refreshing the DB in pgAdmin or creating queries with psql to make sure the requests work as intended
  • -
    +

    Summing Things Up

    • Now the API has been hooked up to a PostgreSQL database
    • @@ -390,7 +396,7 @@ services.AddControllers().AddNewtonsoftJson();
    • Controllers accepting HTTP requests have access to the repository
    -
    +

    EFCore Code First Checklist

    1. Install required packages
    2. @@ -400,7 +406,7 @@ services.AddControllers().AddNewtonsoftJson();
    3. Add CRUD operations to the database repository
    -
    +

    Modifying the Relations

    • Let's change the structure of our Contacts API by adding a new class Account @@ -412,7 +418,7 @@ services.AddControllers().AddNewtonsoftJson();
    • Emails will be removed from the Contacts table
    -
    +
    // Models/Contact.cs
     public class Contact
     {
    @@ -432,13 +438,13 @@ services.AddControllers().AddNewtonsoftJson();
     }
     
    -
    +
    • Adding a migration at this point will result in a warning:
    -
    +
      @@ -460,7 +466,7 @@ services.AddControllers().AddNewtonsoftJson();
    -
    +

    Exercise 3: Adding Migrations

    Continue working on CourseAPI.

    @@ -471,10 +477,10 @@ services.AddControllers().AddNewtonsoftJson();
  • Update the database. Check that the changes show up in the database with pgAdmin
  • -
    +

    Database First approach

    -
    +

    What is the Database First approach?

    • This is the other approach for creating a connection between the database and the application
    • @@ -489,7 +495,7 @@ services.AddControllers().AddNewtonsoftJson();
    -
    +

    Scaffolding

    • Use the Package Manager Console to "reverse engineer" the code for an existing database @@ -503,7 +509,7 @@ services.AddControllers().AddNewtonsoftJson();
    • Using the connection string corresponding to your database, this will create all the classes for the entities in the DB as well as the context class
    -
    +

    Exercise 4: Database First

    Create a new ASP.NET Core web app using the API template.

    @@ -514,13 +520,13 @@ b) by copying the <PackageReference> lines from the .cs
  • Scaffold the sqlpractice database created in SQL Databases Exercise 1 to the project by using the Database First approach. If you have not yet created the database in PostgreSQL, it can be found here
  • -
    +

    Reading: Authentication with roles

    • Here's an example how to do a role-based authentication by using JWT tokens
    -
    +

    Exercise 5 (Extra): Connection

    Continuing the previous exercise,

    diff --git a/6-databases-with-entity-framework.md b/6-databases-with-entity-framework.md index c07ddfc..67ccb11 100644 --- a/6-databases-with-entity-framework.md +++ b/6-databases-with-entity-framework.md @@ -16,11 +16,14 @@ title: 5. Databases with Entity Framework - [Entity Framework (EF)](#entity-framework-ef) - [Code First approach](#code-first-approach) - [Migrations](#migrations) +- [Migrations](#migrations) - [Database First approach](#database-first-approach) ## Entity Framework (EF) +* This lecture will assume you have a basic understanding of SQL databases + * Read [Webdev basics: SQL Databases](https://gitea.buutti.com/education/webdev-basics/src/branch/main/sql-databases.md) first! * [Entity Framework](https://learn.microsoft.com/en-us/ef/) is an Object-Relational Mapper (ORM) made by Microsoft for the .NET framework * Object-Relational Mapping: converting from database representation to objects in a programming language * Allows creation of CRUD operations without writing SQL diff --git a/README.md b/README.md index 0101627..16ff1d3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ | 3 | [MVP Pattern and Repositories](3-mvc-pattern-and-repositories.md) | [Download slides](3-mvc-pattern-and-repositories-slides.html?raw=1) | | 4 | [RESTful HTTP Methods](4-restful-http-methods.md) | [Download slides](4-restful-http-methods-slides.html?raw=1) | | 5 | [Model Validation & API Design](5-model-validation-and-designing-apis.md) | [Download slides](5-model-validation-and-designing-apis-slides.html?raw=1) | -| | [SQL Databases](sql-databases.md) | [Download slides](5-model-validation-and-designing-apis-slides.html?raw=1) | | 6 | [Databases with Entity Framework](6-databases-with-entity-framework.md) | [Download slides](6-databases-with-entity-framework-slides.html?raw=1) | | 7 | [Authentication and Authorization](7-authentication-and-authorization.md) | [Download slides](7-authentication-and-authorization-slides.html?raw=1) | | 8 | [Testing](8-testing.md) | [Download slides](8-testing-slides.html?raw=1) | diff --git a/imgs/7-databases-with-entity-framework_0.png b/imgs/7-databases-with-entity-framework_0.png deleted file mode 100644 index 3fb3c4a..0000000 Binary files a/imgs/7-databases-with-entity-framework_0.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_1.png b/imgs/7-databases-with-entity-framework_1.png deleted file mode 100644 index 4b397ea..0000000 Binary files a/imgs/7-databases-with-entity-framework_1.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_2.png b/imgs/7-databases-with-entity-framework_2.png deleted file mode 100644 index 2c8f562..0000000 Binary files a/imgs/7-databases-with-entity-framework_2.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_3.png b/imgs/7-databases-with-entity-framework_3.png deleted file mode 100644 index 9e7dee7..0000000 Binary files a/imgs/7-databases-with-entity-framework_3.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_4.png b/imgs/7-databases-with-entity-framework_4.png deleted file mode 100644 index 03a1e2f..0000000 Binary files a/imgs/7-databases-with-entity-framework_4.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_5.png b/imgs/7-databases-with-entity-framework_5.png deleted file mode 100644 index 2d57d7c..0000000 Binary files a/imgs/7-databases-with-entity-framework_5.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_6.png b/imgs/7-databases-with-entity-framework_6.png deleted file mode 100644 index 23fd825..0000000 Binary files a/imgs/7-databases-with-entity-framework_6.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_7.png b/imgs/7-databases-with-entity-framework_7.png deleted file mode 100644 index d0555d8..0000000 Binary files a/imgs/7-databases-with-entity-framework_7.png and /dev/null differ diff --git a/imgs/7-databases-with-entity-framework_8.png b/imgs/7-databases-with-entity-framework_8.png deleted file mode 100644 index 30401dc..0000000 Binary files a/imgs/7-databases-with-entity-framework_8.png and /dev/null differ diff --git a/sql-databases-slides.html b/sql-databases-slides.html deleted file mode 100644 index adfaefb..0000000 --- a/sql-databases-slides.html +++ /dev/null @@ -1,431 +0,0 @@ -SQL Databases
    -

    SQL Databases

    -
    -
    -

    What is SQL?

    -
      -
    • SQL, Structured Query Language is a language used to organize and manipulate data in a database
    • -
    • Originally developed by IBM in the 70's -
        -
      • Quickly became the most popular database language
      • -
      -
      SELECT id, email
      -FROM users
      -WHERE first_name = 'Teppo';
      -
      -
    • -
    -
    -
    -

    Relational Database Management Systems

    -
      -
    • In relational databases, values are stored in tables -
        -
      • Each table has rows and columns
      • -
      • Data is displayed in a two-dimensional matrix
      • -
      -
    • -
    • Values in a table are related to each other -
        -
      • Values can also be related to values in other tables
      • -
      -
    • -
    • A relational database management system (RDBMS) is a program that executes queries to relational databases
    • -
    -
    -
    -

    -

    https://db-engines.com/en/ranking

    -
    -
    -

    PostgreSQL

    -
      -
    • Free and open-source, cross-platform relational database management system
    • -
    • Emphasizes extensibility and SQL compliance
    • -
    • Fully ACID-compliant (atomicity, consistency, isolation and durability)
    • -
    • Used in conjunction with pgAdmin -
        -
      • Administration and development platform for PostgreSQL
      • -
      • Cross-platform, features a web interface
      • -
      • Basically a control panel application for your PostgreSQL database
      • -
      -
    • -
    -
    -
    -

    PostgreSQL: Creating a Database

    -
    -
    -

    With pgAdmin:

    -
      -
    1. Right-click Servers > my-postgres > Databases
    2. -
    3. Select Create > Database...
    4. -
    5. Insert a name for the database and hit Save
    6. -
    -
    -
    -

    With psql:

    -
      -
    1. Enter command
      CREATE DATABASE <database-name>;
      -
      -
    2. -
    -
    -
    -
    -

    -
    -
    -
    -

    PostgreSQL: Querying

    -

    With pgAdmin:

    -
      -
    1. Right-click sqlpractice > Query tool...
    2. -
    3. Insert a query into the Query Editor and hit Execute (F5)
    4. -
    -
    -
    -

    -
    -
    -

    -
    -
    -
    -
    -

    With psql:

    -
      -
    1. List all available databases with \l
    2. -
    3. Connect to the created database with \c <database-name>
    4. -
    5. List all tables in the database with \dt
    6. -
    7. Type a query and press enter - -
    8. -
    -
    -
    -

    Editing Data with pgAdmin

    -
    -
    -
      -
    • Inspect and edit data in pgAdmin by right-clicking a table and selecting View/Edit Data
    • -
    -
    -
    -

    -
    -
    -
    -
    -
    -
    -
      -
    • Individual values in the table can be directly modified by double clicking the value and then editing the value in the visual user interface
    • -
    • Save the changes by pressing the Save Data Changes button
    • -
    -
    -
    -


    -

    -
    -
    -
    -
    -

    Exercise 1: Preparing the database

    - -

    Using either PgAdmin or PSQL,

    -
      -
    1. Create a new database called sqlpractice
    2. -
    3. Insert the provided example query to the new database
    4. -
    5. Verify that the query has created new tables to your database
    6. -
    -
    -
    -

    Basic SQL queries

    -
      -
    • SELECT
    • -
    • INSERT
    • -
    • DELETE
    • -
    • UPDATE
    • -
    • CREATE & DROP
    • -
    -
    -
    -

    Querying data with SELECT

    -
      -
    • Syntax:
      SELECT column1, column2, column3 FROM table_name;
      -
      -
    • -
    • Examples:
      SELECT full_name, email FROM users;
      -SELECT full_name AS name, email FROM users;
      -SELECT * FROM users;
      -
      -
    • -
    -
    -
    -

    Filtering data with WHERE

    -
      -
    • Syntax:
      SELECT column1, column2 FROM table_name WHERE condition;
      -
      -
    • -
    -
    -
    -
      -
    • Text is captured in single quotes.
    • -
    • LIKE condition uses the % sign as a wildcard.
    • -
    • IS and IS NOT are also valid operators.
    • -
    -
    -
    -
      -
    • Example:
      SELECT full_name FROM users
      -WHERE full_name = 'Teppo Testaaja';
      -
      -SELECT * FROM books WHERE name LIKE '%rr%';
      -SELECT * FROM books WHERE author IS NOT null;
      -
      -
    • -
    -
    -
    -
    -
    -

    Ordering data with ORDER BY

    -
      -
    • Syntax:
      SELECT column1 FROM table_name ORDER BY column1 ASC;
      -
      -
    • -
    • Examples:
      SELECT full_name FROM users
      -ORDER BY full_name ASC;
      -
      -SELECT full_name FROM users
      -ORDER BY full_name DESC;
      -
      -
    • -
    -
    -
    -

    Combining data with JOIN

    -
      -
    • Also known as INNER JOIN
    • -
    • Corresponds to intersection from set theory
    • -
    -
    -

    -
    -
    -
    -

    JOIN examples

    -
    SELECT
    -users.id, users.full_name, borrows.id,
    -borrows.user_id, borrows.due_date, borrows.returned_at
    -FROM users
    -JOIN borrows ON
    -users.id = borrows.user_id;
    -
    -
    SELECT
    -U.full_name AS name,
    -B.due_date AS due_date,
    -B.returned_at AS returned_at
    -FROM users AS U
    -JOIN borrows AS B ON
    -U.id = B.user_id;
    -
    -
    -
    -

    Combining with LEFT JOIN

    -
    -
    -
      -
    • Also known as LEFT OUTER JOIN
    • -
    • Example:
      SELECT
      -U.full_name AS name,
      -B.due_date AS due_date,
      -B.returned_at AS returned_at
      -FROM users AS U
      -LEFT JOIN borrows AS B ON
      -U.id = B.user_id;
      -
      -
    • -
    -
    -
    -

    -
    -
    -
    -
    -

    Exercise 2: Querying the library

    - -

    Using SQL queries, get

    -
      -
    1. all columns of borrows that are borrowed before 2020-10-27
    2. -
    3. all columns of borrows that are returned
    4. -
    5. columns user.full_name and borrows.borrowed_at of the user with an id of 1
    6. -
    7. columns book.name, book.release_year and language.name of all books that are released after 1960
    8. -
    -
    -
    -

    Inserting data with INSERT

    -
      -
    • Syntax
      INSERT INTO table_name (column1, column2, column3) VALUES (value1, value2, value3);
      -
      -
    • -
    • Example
      INSERT INTO users (full_name, email, created_at)
      -VALUES ('Pekka Poistuja', 'pekka.poistuja@buutti.com', NOW());
      -
      -
    • -
    • Since id is not provided, it will be automatically generated.
    • -
    -
    -
    -

    Updating data with UPDATE

    -
      -
    • Syntax
      UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition;
      -
      -
    • -
    • Notice: if a condition is not provided, all rows will be updated! -
        -
      • If updating only one row, it is usually best to use id.
      • -
      -
    • -
    • Example
      UPDATE users SET email = 'taija.testaaja@gmail.com' WHERE id = 2;
      -
      -
    • -
    -
    -
    -

    Removing data with REMOVE

    -
      -
    • Syntax
      DELETE FROM table_name WHERE condition;
      -
      -
    • -
    • Again, if the condition is not provided, DELETE affects all rows
    • -
    • Before deleting, it is a good practice to execute an equivalent SELECT query to make sure that only the proper rows will be affected.
    • -
    • Example:
      SELECT * FROM users WHERE id = 5;
      -DELETE FROM users WHERE id = 5;
      -
      -
    • -
    -
    -
    -

    Exercise 3: Editing data

    - -
      -
    1. Postpone the due date of the borrow with an id of 1 by two days in the borrows table
    2. -
    3. Add a couple of new books to the books table
    4. -
    5. Delete one of the borrows.
    6. -
    -
    -
    -

    Initializing data with CREATE TABLE

    -
      -
    • Before data can be manipulated, a database and its tables need to be initialized.
    • -
    -
    -
    -
      -
    • Syntax
      CREATE TABLE table_name (
      -  column1 datatype,
      -  column2 datatype,
      -  ...
      -);
      -
      -
    • -
    -
    -
    -
      -
    • Example:
      CREATE TABLE "users" (
      -  "id" SERIAL PRIMARY KEY,
      -  "full_name" varchar NOT NULL,
      -  "email" varchar UNIQUE NOT NULL,
      -  "created_at" timestamp NOT NULL
      -);
      -
      -
    • -
    -
    -
    -
    -
    -

    Removing data with DROP

    -
      -
    • In order to remove tables or databases, we use a DROP statement
      DROP TABLE table_name;
      -DROP DATABASE database_name;
      -
      -
    • -
    • These statements do not ask for confirmation and there is no undo feature. Take care when using a drop statement.
    • -
    -
    -
    -
      -
    • I'm legally obliged to include this XKCD comic here.
    • -
    -

    -
    -
    -

    NoSQL

    -
      -
    • In addition to SQL databases, there's NoSQL
    • -
    • There are many differing definitions, but... -
        -
      • most agree that NoSQL databases store data in a format other than tables
      • -
      • They can still store relational data - just differently
      • -
      -
    • -
    • Four different database types: -
        -
      • Document databases
      • -
      • Key-value databases
      • -
      • Wide-column stores
      • -
      • Graph databases
      • -
      -
    • -
    • Example database engines include MongoDB, Redis and Cassandra
    • -
    • For more info, see MongoDB: What is NoSQL?
    • -
    -
    -
    -

    Object-Relational Mappers

    -
      -
    • Object-Relational Mappers, or ORMs allow the developer to write code instead of SQL to perform CRUD operations on their database
    • -
    • An abstraction layer between code and database -
        -
      • Can make database logic easier to read and write
      • -
      • Prevents SQL injection by sanitizing input (see the comic before...)
      • -
      • ...but sometimes you'd rather just write the SQL queries themselves
      • -
      -
    • -
    • Some popular ORMs: -
        -
      • Hibernate (Java)
      • -
      • EFCore (.NET)
      • -
      • Sequelize (Node.js)
      • -
      • TypeORM (TypeScript)
      • -
      -
    • -
    -
    -
    \ No newline at end of file diff --git a/sql-databases.md b/sql-databases.md deleted file mode 100644 index 6bedbd8..0000000 --- a/sql-databases.md +++ /dev/null @@ -1,400 +0,0 @@ ---- -marp: true -paginate: true -math: mathjax -theme: buutti -title: SQL Databases ---- - -# SQL Databases - - - - -## What is SQL? - -* SQL, Structured Query Language is a language used to organize and manipulate data in a database -* Originally developed by IBM in the 70's - * Quickly became the most popular database language - ```sql - SELECT id, email - FROM users - WHERE first_name = 'Teppo'; - ``` - -## Relational Database Management Systems - -* In relational databases, values are stored in *__tables__* - * Each table has *__rows__* and *__columns__* - * Data is displayed in a two-dimensional matrix -* Values in a table are related to each other - * Values can also be related to values in other tables -* A relational database management system (RDBMS) is a program that executes queries to relational databases - ---- - -![](imgs/7-databases-with-entity-framework_0.png) - -[https://db-engines.com/en/ranking](https://db-engines.com/en/ranking) - -## PostgreSQL - -* Free and open-source, cross-platform relational database management system -* Emphasizes extensibility and SQL compliance -* Fully ACID-compliant (atomicity, consistency, isolation and durability) -* Used in conjunction with ***pgAdmin*** - * Administration and development platform for PostgreSQL - * Cross-platform, features a web interface - * Basically a control panel application for your PostgreSQL database - -### PostgreSQL: Creating a Database - -
    -
    - -With pgAdmin: -1) Right-click *Servers > my-postgres > Databases* -2) Select _Create > Database..._ -3) Insert a name for the database and hit _Save_ - -
    -
    - -With psql: -1) Enter command - ```sql - CREATE DATABASE ; - ``` - -
    -
    - -
    - -![](imgs/7-databases-with-entity-framework_1.png) - -
    - -### PostgreSQL: Querying - -With pgAdmin: - -1) Right-click _sqlpractice > Query tool..._ -2) Insert a query into the _Query Editor_ and hit _Execute_ (F5) - -
    -
    - -![w:345](imgs/7-databases-with-entity-framework_2.png) - -
    -
    - -![](imgs/7-databases-with-entity-framework_3.png) - -
    -
    - ---- - -With psql: - -1) List all available databases with `\l` -2) Connect to the created database with `\c ` -3) List all tables in the database with `\dt` -4) Type a query and press enter - * Here's an [example query](code-examples/example-query.sql) - -## Editing Data with pgAdmin - -
    -
    - -* Inspect and edit data in pgAdmin by right-clicking a table and selecting _View/Edit Data_ - -
    -
    - -![](imgs/7-databases-with-entity-framework_4.png) - -
    -
    - ---- - -
    -
    - -* Individual values in the table can be directly modified by double clicking the value and then editing the value in the visual user interface -* Save the changes by pressing the _Save Data Changes_ button - -
    -
    - -![](imgs/7-databases-with-entity-framework_5.png) -![](imgs/7-databases-with-entity-framework_6.png) - -
    -
    - -## Exercise 1: Preparing the database - - -Using either PgAdmin or PSQL, - -1) Create a new database called `sqlpractice` -2) Insert the provided [example query](code-examples/example-query.sql) to the new database -3) Verify that the query has created new tables to your database - -## Basic SQL queries - -* `SELECT` -* `INSERT` -* `DELETE` -* `UPDATE` -* `CREATE` & `DROP` - -### Querying data with `SELECT` - -* Syntax: - ```sql - SELECT column1, column2, column3 FROM table_name; - ``` -* Examples: - ```sql - SELECT full_name, email FROM users; - SELECT full_name AS name, email FROM users; - SELECT * FROM users; - ``` - -### Filtering data with `WHERE` - -* Syntax: - ```sql - SELECT column1, column2 FROM table_name WHERE condition; - ``` -
    -
    - -* Text is captured in **_single quotes_**. -* `LIKE` condition uses the `%` sign as a wildcard. -* `IS` and `IS NOT` are also valid operators. - -
    -
    - -* Example: - ```sql - SELECT full_name FROM users - WHERE full_name = 'Teppo Testaaja'; - - SELECT * FROM books WHERE name LIKE '%rr%'; - SELECT * FROM books WHERE author IS NOT null; - ``` - -
    -
    - -### Ordering data with `ORDER BY` - -* Syntax: - ```sql - SELECT column1 FROM table_name ORDER BY column1 ASC; - ``` -* Examples: - ```sql - SELECT full_name FROM users - ORDER BY full_name ASC; - - SELECT full_name FROM users - ORDER BY full_name DESC; - ``` - -### Combining data with `JOIN` - -* Also known as `INNER JOIN` -* Corresponds to ***intersection*** from [set theory](https://en.wikipedia.org/wiki/Set_theory) - -
    - -![w:500px](imgs/7-databases-with-entity-framework_7.png) - -
    - -#### JOIN examples - -```sql -SELECT -users.id, users.full_name, borrows.id, -borrows.user_id, borrows.due_date, borrows.returned_at -FROM users -JOIN borrows ON -users.id = borrows.user_id; -``` - -```sql -SELECT -U.full_name AS name, -B.due_date AS due_date, -B.returned_at AS returned_at -FROM users AS U -JOIN borrows AS B ON -U.id = B.user_id; -``` - -### Combining with `LEFT JOIN` - -
    -
    - -* Also known as `LEFT OUTER JOIN` -* Example: - ```sql - SELECT - U.full_name AS name, - B.due_date AS due_date, - B.returned_at AS returned_at - FROM users AS U - LEFT JOIN borrows AS B ON - U.id = B.user_id; - ``` - -
    -
    - -![](imgs/7-databases-with-entity-framework_8.png) - -
    -
    - -### Exercise 2: Querying the library - - -Using SQL queries, get - -1) all columns of `borrows` that are borrowed before `2020-10-27` -2) all columns of `borrows` that are returned -3) columns `user.full_name` and `borrows.borrowed_at` of the user with an `id` of 1 -4) columns `book.name`, `book.release_year` and `language.name` of all books that are released after 1960 - -### Inserting data with `INSERT` - -* Syntax - ```sql - INSERT INTO table_name (column1, column2, column3) VALUES (value1, value2, value3); - ``` -* Example - ```sql - INSERT INTO users (full_name, email, created_at) - VALUES ('Pekka Poistuja', 'pekka.poistuja@buutti.com', NOW()); - ``` -* Since id is not provided, it will be automatically generated. - -### Updating data with `UPDATE` - -* Syntax - ```sql - UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition; - ``` -* *__Notice:__* if a condition is not provided, all rows will be updated! - * If updating only one row, it is usually best to use `id`. -* Example - ```sql - UPDATE users SET email = 'taija.testaaja@gmail.com' WHERE id = 2; - ``` - -## Removing data with `REMOVE` - -* Syntax - ```sql - DELETE FROM table_name WHERE condition; - ``` -* Again, if the _condition_ is not provided, `DELETE` affects _all_ rows -* Before deleting, it is a good practice to execute an equivalent `SELECT` query to make sure that only the proper rows will be affected. -* Example: - ```sql - SELECT * FROM users WHERE id = 5; - DELETE FROM users WHERE id = 5; - ``` - -### Exercise 3: Editing data - - -1) Postpone the due date of the borrow with an `id` of `1` by two days in the `borrows` table -2) Add a couple of new books to the `books` table -3) Delete one of the borrows. - -### Initializing data with `CREATE TABLE` - -* Before data can be manipulated, a database and its tables need to be initialized. - -
    -
    - -* Syntax - ```sql - CREATE TABLE table_name ( - column1 datatype, - column2 datatype, - ... - ); - ``` - -
    -
    - -* Example: - ```sql - CREATE TABLE "users" ( - "id" SERIAL PRIMARY KEY, - "full_name" varchar NOT NULL, - "email" varchar UNIQUE NOT NULL, - "created_at" timestamp NOT NULL - ); - ``` - -
    -
    - -### Removing data with `DROP` - -* In order to remove tables or databases, we use a `DROP` statement - ```sql - DROP TABLE table_name; - DROP DATABASE database_name; - ``` -* These statements do not ask for confirmation and there is no undo feature. Take care when using a drop statement. - ---- - -* I'm legally obliged to include this XKCD comic here. - -![](https://imgs.xkcd.com/comics/exploits_of_a_mom_2x.png) - -## NoSQL - -* In addition to SQL databases, there's ***NoSQL*** -* There are many differing definitions, but... - * most agree that NoSQL databases store data in a format other than tables - * They can still store relational data - just _differently_ -* Four different database types: - * Document databases - * Key-value databases - * Wide-column stores - * Graph databases -* Example database engines include [MongoDB](https://www.mongodb.com/), [Redis](https://redis.io/) and [Cassandra](https://cassandra.apache.org/_/index.html) -* For more info, see [MongoDB: What is NoSQL?](https://www.mongodb.com/resources/basics/databases/nosql-explained) - - -## Object-Relational Mappers - -* Object-Relational Mappers, or ORMs allow the developer to write code instead of SQL to perform CRUD operations on their database -* An abstraction layer between code and database - * Can make database logic easier to read and write - * Prevents SQL injection by sanitizing input (see the comic before...) - * ...but sometimes you'd rather just write the SQL queries themselves -* Some popular ORMs: - * Hibernate (Java) - * EFCore (.NET) - * Sequelize (Node.js) - * TypeORM (TypeScript)