onyx-http

REST API framework with type-safe params and separate business and rendering layers, based on onyx-http web framework router websockets modular http-handler onyxframework
0.2.0-pre.2 released
onyxframework/http
141 11 7
Onyx Framework

Prism is an expressive modular web framework.

Build Status Docs Dependencies GitHub release

Why

  • Because modular approach is better than singleton configurations.
  • Because params need easy validation and typecasting.
  • Because essentials like CORS and authorization should be in one place.

Contents

  1. Installation
  2. Usage
    1. Basic example with params
    2. Auth
    3. ⚡️ Websockets

Installation

Add this to your application's shard.yml:

dependencies:
  prism:
    github: vladfaust/prism

This shard follows Semantic Versioning v2.0.0.

Usage

Please refer to the documentation available online at vladfaust.com/prism.

Basic example with params

require "logger"
require "prism"

struct GetUser < Prism::Action
  include Params # Enable params in this action

  params do
    param :id, Int32, validate: {min!: 0} # Require "id" parameter, cast it to Int32 and validate it is greater than 0
    param :display_info, Bool? # This parameter is optional
  end

  def call
    typeof(params[:id]) # => Int32
    typeof(params[:display_info]) # => Bool?

    user = User.where(id: params[:id])
    halt!(404, "User not found") unless user # Will abort further execution

    user.info = "blah" if params[:display_info]

    # Print JSON represenation of the user (call `#to_json` on it)
    json(user)
  end
end

router = Prism::Router.new do |r|
  r.get "/" do |env|
    env.response.print("Hello world!") # Simple
  end

  r.get "/users/:id" do |env|
    GetUser.call(env) # Action call, :id will be casted to Int32 param (see above)
  end

  r.on "/users/:id", methods: %w(post put) do |env|
    typeof(env.request.path_params) # => Hash(String, String), no typecasting
    env.response.print("Will update user #{env.request.path_params["id"]}")
  end
end

logger = Logger.new(STDOUT)

server = Prism::Server.new("localhost", 5000, [Prism::Logger.new(logger), router], logger)
server.listen

#  INFO -- :   Prism server v0.1.0 is listening on http://localhost:5000...

Auth

Prism::Authable, Prism::ProcHandler together allow to define authentication logic in no time!

class Auth < Prism::Authable
  @user : User? = nil
  getter! user

  def initialize(@token : String)
  end

  # This method will be lazily called on `auth!` (see below)
  def auth
    @user = find_user_by_token(@token)
  end
end

struct StrictAction < Prism::Action
  include Auth # Enable auth in this action

  auth! # Halt 401 unless authorized

  def call
    # Basically, `auth.user` equals to `context.request.auth.not_nil!.user` (see below)
    json(auth.user) # Yep, just like that
  end
end

struct ConditionalAction < Prism::Action
  include Auth

  def call
    if auth?
      json(auth.user)
    else
      text("Hey, you're not authed")
    end
  end
end

# Add this handler to handlers list before or after router
# Will extract token from "?token=xyz" query param
auth = Prism::ProcHandler.new do |handler, context|
  if (token = context.request.query_params.to_h["token"]?)
    context.request.auth = Auth.new(token) # No magic, just science
  end

  handler.call_next(context)
end

Websockets

We call them Channels for convenience.

require "prism/channel"

class Notifications < Prism::Channel
  @@subscriptions = Array(self).new # It's a custom code

  # It's a custom code as well
  def self.notify(message)
    @@subscriptions.each do |sub|
      sub.socket.send(message)
    end
  end

  # This is one of the default callbacks
  def on_open
    socket.send("Hola")
    @@subscriptions.push(self)
  end

  # ditto
  def on_close
    @@subscriptions.delete(self)
  end
end

router = Prism::Router.new do |r|
  r.ws "/notifications" do |socket, env|
    Notifications.subscribe(socket, env)
  end
end

# Later in the code...

Notifications.notify("A message") # Will notify all subscribers

Remember that websockets are bound to this particular Crystal process only!

Contributing

  1. Fork it ( https://github.com/vladfaust/prism/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

onyx-http:
  github: onyxframework/http
  version: ~> 0.2.0-pre.2
License MIT
Crystal 0.23.1

Authors

Dependencies 3

  • radix ~> 0.3.8
    {'github' => 'luislavena/radix', 'version' => '~> 0.3.8'}
  • shard ~> 0.2.0
    {'github' => 'maiha/shard.cr', 'version' => '~> 0.2.0'}
  • time_format ~> 0.1.0
    {'github' => 'vladfaust/time_format.cr', 'version' => '~> 0.1.0'}

Development Dependencies 0

Dependents 2

Last synced .
search fire star recently