RESTrict Framework#

The RESTrict Framework is a different way to think about constructing Web applications. It defines a new paradigm, functional-declarative hybrid, resource-oriented development, a hybrid of declarative and functional programming. It is the hope that this new paradigm engenders a new mental model for Web applications, one that more naturally fits the hypertext as the engine of application state.

The blog example#

The “Hello, World!” of applications is a simple blogging site. Making this application with the RESTrict Framework takes three steps.

1. Declare the resources#

To declare your resources, you define .resource or .resources files that contain the shape of the data for your resources, how they relate to one another, and who can access the different actions on the resources.

Danger

Code subject to change The RESTrict Framework is currently in its early design and development phase. The following code sample is subject to change at any time.

Listing 1 post.resources#
refer to <security/auth/starlette> as saa
refer to <security/auth/password> as sap
use <types/scalar>
use <types/text>
use <types/time>

override saa.StarletteAuthentication {
  dnc {
    <party:1> principal: Person
  }
}

party Person {
  name: Text(1,100)
  email: unique sap.Email
  is_admin: boolean
  password: hidden sap.Password

  security {
    mutators {
      create: request.actor.is_authenticated |> not() |> or(request.actor.is_admin)
      *: request.actor |> eq(self) |> or(request.actor.is_admin)
    }
    accessors {
      <entrypoint> list: request.actor.is_admin
      details: {
        name: true
        *: request.actor |> eq(self) |> or(request.actor.is_admin)
      }
    }
  }
}

role Writer {
  author: assignment.person
  active: now |> gte(assignment.assigned) |> lte(assignment.unassigned)

  dnc {
    <previous:1> assignment: WriterAssignment
  }

  security {
    mutators {
      *: request.actor.is_admin
    }
    accessors {
      <entrypoint> list: request.actor.is_admin
      details: request.actor.is_admin
    }
  }
}

interval WriterAssignment {
  assigned: Timestamp
  unassigned: optional Timestamp : value |> gte(assigned)
  
  dnc {
    <party:1> person: Person
    <next:1> writer: Writer
  }

  effects {
    create {
      assigned: now
      writer: transact create Writer { assignment: self }
    }
  }

  security {
    mutators {
      *: request.actor.is_admin
    }

    accessors {
      *: request.actor.is_admin
    }
  }
}

moment Post {
  title: Text(1,100)
  published: optional Timestamp
  content: Markdown

  dnc {
    <role:1> writer: Writer
    <next:*> comments: page Comment
  }

  security {
    mutators {
      create: request.actor.roles |> filter(x | x is Writer) |> any(x | x.active)
      modify: request.actor = writer.author
      delete: request.actor = writer.author
    }

    accessors {
      <entrypoint> list: now |> lte(published) |> or(reqeust.actor.is_admin)
      details: published |> gte(now) |> or(request.actor.is_admin)
    }
  }
}

moment Comment {
  author: readonly optional Person
  submitted: readonly Timestamp
  content: readonly Markdown(p, ul, ol, li)

  dnc {
    <party:1> author: optional Person
    <previous:1> post: Post
  }

  effects {
    create {
      author: request.actor
      submitted: now
      content: request.body.content
    }
  }

  security {
    accessors {
      details: {
        author: request.actor.is_authenticated
      }
    }

    mutators {
      delete: request.actor |> in([author, post.author])
      modify: request.actor = author
      create: true
    }
  }
}

2. Start the application#

Install and run the application using Python 3.11 or newer.

pip install --user restrict-framework
python -m restrict.framework --resources post.resources 

3. Use the API#

Use REST, GraphQL, and WebSockets to interact with the API.