SkillAgentSearch skills...

Stampede

🦕 Deno REST framework/eco-system

Install / Use

/learn @bashovski/Stampede
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <img alt="Stampede" src="ui/src/assets/logo.svg" /> </p> <p align="center"> <a href="https://img.shields.io/badge/license-MIT-%23339933"><img src="https://img.shields.io/badge/license-MIT-%23339933" alt="License"></a> <a href="https://img.shields.io/github/contributors/bashovski/stampede?color=%23011762"><img src="https://img.shields.io/github/contributors/bashovski/stampede?color=%23011762" alt="Contributors"></a> </p>

Stampede 🦕

  • To see the source code of Stampede CLI, check https://github.com/bashovski/stampede-cli

About

  • Stampede is a framework, or an eco-system written in TypeScript for Deno, made with emphasis on delivering new features quicker than ever before.

Features

  • CLI - generate modules instantly and make progress without fuss!
  • Convenient M(V)CS structure - strong emphasis on separation of concerns
  • Autowired modules and loaders - implicitly run codebase without piling up large imports
  • Authentication system ready for use
  • Modified Koa Router utilization - easy-to-use Routing, HTTP request handlers, etc.
  • Deployment configuration which is production-ready
  • View layer written in Vue.js - includes auth pages, guards, etc.
  • DenoDB ORM that supports PostgreSQL
  • Pre-written migrations with Umzug and Sequelize (there's raw SQL as well)
  • Feature Flags - toggle what features should be running during server runtime (configurable even during runtime)
  • Autowired config - adding and consuming environment variables kept elegant
  • Multi-level logger with DataDog support (allows usage of facets and tags)
  • Custom Mail module/internal micro-library compatible with SendGrid
  • Validators - validate and restrict invalid user input with large variety of options
  • Factories - populate your database with false records (powered by faker.js)
  • Route Guards for your Vue SPA - modify the behavior of page access layer the way you want to
  • Autowired CRON jobs
  • Unit tests with bootstrappers
  • Insomnia and Swagger doc for existing REST API endpoints
  • and many more...

Requirements

| Service | Min. version | |------------|--------------| | Deno | 1.3.0 | | V8 | 8.6.334 | | TypeScript | 3.9.7 | | Node¹ | 12.6.3 | | NPM¹ | 6.14.4 |

¹ - Not mandatory, used only for migrations with Umzug.

Installation

  • Clone the repository:<br/>
git clone https://github.com/bashovski/stampede
  • Cd into the project directory:<br/>
cd stampede
  • Create .env file using .env.example as a base:<br/>
cp .env.example .env
  • Update environment variables in .env file. (DB_*) fields are mandatory.<br/> You can, but you don't have to set up local PostgreSQL Docker container for dev purposes as it's very easy to use by running:
docker run --name stampede-project-db -e POSTGRES_PASSWORD=your_db_password -d -p 5432:5432 postgres

If you already have Postgres service up and running, you can avoid the command above.<br/>

  • In order for programmatic migrations to be ran, please install Node dependencies: npm i
  • Once they are installed, execute the run command to start up the server:
./scripts/run
  • Your server will be up and running now.
  • After booting the server, you can serve UI written in Vue.js:
  • Open a separate terminal window/tab and run the command from root project directory:
cd ui && npm i
npm run serve

You can normally run the UI serve script once you have dependencies installed for it:

./scripts/ui # From root project directory
  • By default, the server will be accessible at: http://localhost:80/, <br/> and the UI will be accessible from http://localhost:8080

  • Now, as you have both the server and client up and running, feel free to read how Stampede works and check out some of the tutorials and blogs!

Models

  • Each table in a database has a corresponding model.

  • Using DenoDB ORM, you may apply mutations to the model's instances, persist new records, or remove them from the database.

  • We can view models as blueprints for certain objects we want to store and consume in our application.

  • Using Stampede CLI, you can easily create new model: stampede model Video - creates Video.ts model in models/ directory.

  • Let's take a look at an example of a well-structured model:

import { DataTypes } from 'https://raw.githubusercontent.com/eveningkid/denodb/abed3063dd92436ceb4f124227daee5ee6604b2d/mod.ts';
import Model from '../lib/Model.ts';

class Video extends Model {

    static table = 'videos'; // Name of the table in DB
    static timestamps = true; // Enables created_at and updated_at columns

    // We'll keep it simple, here are a couple of fields related to the model:
    // For more info regarding usage of DenoDB ORM: https://eveningkid.github.io/denodb-docs/
    static fields = {
        id: {
            type: DataTypes.UUID,
            primaryKey: true
        },
        title: {
            type: DataTypes.STRING,
            length: 256,
            allowNull: false
        },
        channelId: {
            type: DataTypes.UUID,
            allowNull: false
        },
        views: {
            type: DataTypes.INTEGER,
            allowNull: false
        }
    };
}

export default Video;
  • That'd be a very basic model example. In order for the model metadata to be appended to a database, you'll need to create a migration, and assure each field is present in the migration. Please read the 'Migrations' section below.

Routing

  • Once you create a model using CLI, you also create a routing for that model. Routings can always be independent of the model, and therefore do not need to have any association with any model whatsoever. It is advisable to follow the Pascal-case naming convention of modules - e.g. ModelRouting.ts.

As we mentioned earlier, Stampede utilizes Koa router which offers many functionalities and in this case, we'll explain proper usage of routes. Let's use a small chunk of UserRouting.ts:

/*
 * To follow the convention, HTTP requests are being handled by controllers. That's why we are using UserController here.
 * UserController will consume the context argument - passed required headers and sent data to a service (in this case UserService).
 * UserService will process passed data, perhaps update the database and return the response (ServiceResult).
 * Once controller receives response (ServiceResult), it will most likely send the response to the client.
 */
import Router from '../lib/Router.ts';
import UserController from '../controllers/UserController.ts';
import AuthMiddleware from '../middleware/AuthMiddleware.ts';
import HttpResponse from '../http/HttpResponse.ts'; // Only used for last example

/**
 * Now, let's take a look at the login request:
 * Notice how we only require the Router.post function to invoke UserController.loginUser and nothing else.
 * The loginUser function will handle the sending of response and controller -> service transaction prior to that.
 */
Router.post('/users/login', UserController.loginUser);

/**
 * In this example, we want to fetch current user's information - such as username, date of birth, avatar, etc.
 * In order to achieve that, we need to primarily assure that the client which sends the request is authenticated user.
 * Now notice that we are using AuthMiddleware.authenticateUser method before retrieving user's info (IAM).
 * In this case, AuthMiddleware.authenticateUser behaves as a middleware and checks if there's a session with passed session cookie.
 * Obviously, if the cookie isn't passed or if the session with corresponding cookie has expired, the authentication will fail,
 * therefore the user won't be allowed to retrieve their data since service won't be able to identify the user with no linked session.
 */ 
Router.get('/users/iam', AuthMiddleware.authenticateUser, UserController.IAM);

/**
 * Let's take a look at another example, which is pretty much straight-forward:
 * Note that the controller functions are usually like the arrow function below.
 * They obviously consume the ctx argument from which many things can be derived,
 * including headers, query params, request body, cookies, etc.
 *
 * In the example below we are creating an instance of HttpResponse and passing two arguments:
 * first arg: status code
 * second arg: body of the response
 *
 * We use the HttpResponse's send() method which consumes response from context and that's it!
 */
Router.get('/users/count', (ctx: any) => {
    new HttpResponse(200, {
        message: 1000 // trivial
    }).send(ctx.response);
});

Controllers

  • Controllers are responsible for handling HTTP requests and sending adequate responses derived from services to a client.

  • We differentiate two types of responses returned by services: a HttpResponse and a HttpError.

  • Both of them are very similar and in fact, HttpError as a class extends HttpResponse.

  • The emphasis here is on semantics, therefore instance of HttpError class may only have status code which is identified as error status code (400-599).

  • To generate a controller, you can use Stampede CLI: stampede ctrl Profile - creates ProfileController.ts

  • You may also provide more controller names as command arguments simultaneously: stampede ctrl Profile Story Post Follower

  • Let's examine an example of a controller:

import Controller from './Controller.ts';
import PostService from '../services/PostService.ts';
import Logger from '../lib/Logger.ts';

/**
 * @class PostController
 * @summary Handles all Post-related HTTP requests
 */
class PostController extends Controller {

    /**
     * @summary Handles index request
     * @param ctx
     */
    static async index(ctx: any): Promise<void> {
        try {

            /*
             * Notice how we destructure the service result. As 

Related Skills

View on GitHub
GitHub Stars206
CategoryDevelopment
Updated6mo ago
Forks4

Languages

TypeScript

Security Score

92/100

Audited on Sep 15, 2025

No findings