You're reading the Rask v1.0.0 guides.View source
All guides

Chapter 3 — A second feature, and locking it down

Goal: add an Orders feature that shares the same database, then require a login to edit the catalog. You'll write: a second slice under Features/Orders/, sharing the one database.

1. A second feature in the same database

The One Person Framework idea is one database for the whole product — so a second feature does not get a second DbContext. It maps through the one the app already has.

Features/Orders/Order.cs is chapter 2's shape with different fields:


namespace Shop.Features.Orders;

public sealed class Order : Entity<Guid>
{
    private Order() { } // EF Core materialization

    private Order(decimal total, Guid productId, DateTime placed)
    {
        Id = Guid.NewGuid();
        this.Total = total;
        this.ProductId = productId;
        this.Placed = placed;
    }

    public decimal Total { get; private set; }

    public Guid ProductId { get; private set; }

    public DateTime Placed { get; private set; }

    public static Order Create(decimal total, Guid productId, DateTime placed) => new(total, productId, placed);

    public void Update(decimal total, Guid productId, DateTime placed)
    {
        this.Total = total;
        this.ProductId = productId;
        this.Placed = placed;
    }
}

Then the same four companions as before — OrderRequest, OrderConfiguration, the command/handler/page files, and the list page. They're the chapter 2 files with Product swapped for Order — copy them and change the type.

What ties it to the existing database goes in Features/Shared/AppDbContext.cs, next to Products — the slice's namespace, and the set:


using Shop.Features.Orders;   // at the top, beside the Products one

public DbSet<Order> Orders => Set<Order>();   // inside the class

That's the whole of "sharing a database": one context, one connection string, one migration history, however many features you add. Nothing else in the slice knows or cares.

Relating entities. Order.ProductId is a plain foreign key here. To have EF understand it as a relationship, add a navigation property and map it in OrderConfiguration:


entity.HasOne<Product>().WithMany().HasForeignKey(x => x.ProductId);

See Rask.Data for the full relationship shapes.

Migrate

A new table means a new migration:


rask db add AddOrder
rask db update

Run rask dev and browse to /orders — a second working CRUD feature, in the same app.db.

2. Require a login to edit the catalog

Right now anyone can create or delete products. The app already has accounts and a login; let's use them. Rask offers two gates, and you'll use both:

  • [Authorize] on a page — route-level. An anonymous deep-link to the page gets redirected to /login.
  • The Authorize component — content-level. It renders one of its slots depending on who's signed in, without leaving the page.

Gate the write pages

Add [Authorize] (from Microsoft.AspNetCore.Authorization) to the generated create / edit / delete pages — Features/Products/CreateProduct.cs, UpdateProduct.cs, DeleteProduct.cs:


using Microsoft.AspNetCore.Authorization;

[Authorize]                          // ← anonymous users are redirected to /login
[Route("/products/new")]
public sealed partial class CreateProduct : Component { … }

Leave the read-only ProductsPage (/products) public so shoppers can browse.

Hide the "New / Edit / Delete" buttons from anonymous users

Route gating stops direct navigation, but you also don't want to show buttons that will just bounce to the login page. Wrap them in the Authorize component (from Rask.Core.Components), which the accounts battery already uses in Auth/MembersPage.cs:


Authorize[                                 // only rendered for signed-in users
    NavLink.Href(CreateProduct)["New product"]
]

For role-specific bits — say a "Delete" button only admins should see — pass Roles:


Authorize.Roles(["admin"])[ DeleteProductButton(product.Id) ]

Verify

  • /orders renders and creating an order persists it (same app.db as products).
  • Signed out, visiting /products/new redirects to /login; the "New product" button isn't shown on /products.
  • After signing in (register an account first at /register — the first one is the administrator), the create/edit/delete pages and buttons appear and work.

Learn more: authentication · the rask CLI

Next → Chapter 4: Background jobs