Skip to content

fredyk/westack-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

westack-go Documentation

Introduction

westack-go is a modular Go framework designed to simplify the process of building scalable and extensible APIs. It provides utilities for managing data models, routing, and middleware, along with powerful integrations for Swagger documentation, CLI tools, and more.

Key Features

  • Model-Driven Architecture: Define data models with ease and generate APIs automatically.
  • Extensible Datasources: Support for in-memory and MongoDB datasources out of the box.
  • Role Management: Built-in role-based access control (RBAC) support.
  • CLI Utilities: Command-line tools for common development tasks.

Prerequisites

  • Go (version 1.21 or higher)
  • MongoDB (if using MongoDB as a datasource)
  • Basic knowledge of Go programming

Quick Start

Follow these steps to quickly set up and run a simple API:

  1. Create the project directory:

    Create a directory for your project and initialize a Go module:

    mkdir myproject && cd myproject
    go mod init myproject

    This sets up the directory and initializes Go module management for your project.

  2. Install the westack-go CLI:

    Install the CLI tool to simplify project setup and management:

    go install github.com/fredyk/westack-go/v2@latest

    The CLI provides commands like init and generate for rapid development.

  3. Initialize the project:

    Use the CLI to set up the project structure:

    westack-go init .

    This creates the basic structure, including configuration files and directories for models and controllers.

  4. Define a model:

    Create a models/note.json file:

    {
      "name": "Note",
      "base": "PersistedModel",
      "properties": {
        "title": {
          "type": "string",
          "required": true
        },
        "content": {
          "type": "string"
        }
      },
      "casbin": {
        "policies": [
          "$authenticated,*,*,allow",
          "$everyone,*,read,allow",
          "$owner,*,__get__footer,allow"
        ]
      }
    }
  5. Generate the model:

    westack-go generate
  6. Create the main application file:

    Create a main.go file with the following content:

    package main
    
    import (
        "log"
    
        "github.com/fredyk/westack-go/v2/westack"
        "myproject/models"
    )
    
    func main() {
        app := westack.New()
    
        app.Boot(westack.BootOptions{
            RegisterControllers: models.RegisterControllers,
        })
    
        log.Fatal(app.Start())
    }
  7. Run the server:

    go run main.go
  8. Test the API:

    Access the Swagger UI at http://localhost:3000/swagger to test your endpoints.


Core Concepts

Architecture Overview

westack-go is built around the following core components:

  • Models: Define the structure of your data and generate APIs automatically.
  • Datasources: Abstract the details of data storage, supporting MongoDB and in-memory stores.
  • Routing: Manage API endpoints and middleware.
  • Controllers: Centralize business logic.
  • CLI Utilities: Simplify repetitive tasks like generating models and controllers.

Key Components

Models

Models are the backbone of westack-go, defined in JSON files under the models/ directory. These JSON files specify attributes, relationships, and access policies using Casbin. From these definitions, westack-go generates Go struct files with the westack-go generate command.

Example of a JSON Model:

{
  "name": "Note",
  "base": "PersistedModel",
  "properties": {
    "title": {
    "title": {
      "type": "string",
      "required": true
    },
    "content": {
      "type": "string"
    }
  },
  "casbin": {
    "policies": [
      "$authenticated,*,*,allow",
      "$everyone,*,read,allow",
      "$owner,*,__get__footer,allow"
    ]
  }
}

When you run:

westack-go generate

This generates Note in models/note.go (if it does not already exist). The Go file can then be extended for additional functionality without affecting the original JSON definitions.

This dual-layer approach allows developers to:

  • Keep JSON files as the source of truth for relationships and Casbin policies.
  • Extend models in Go for advanced functionality.

Note: In the future, the JSON files may be deprecated, and direct Go struct definitions might become the standard.

By default, westack-go generates the following standard CRUD routes for the Note model:

  • POST /notes: Create a new note
  • GET /notes: Retrieve all notes
  • GET /notes/{id}: Retrieve a specific note by ID
  • PATCH /notes/{id}: Partially update fields of a specific note
  • DELETE /notes/{id}: Delete a specific note by ID

Relating Models

You can relate models using the relations property in the JSON definition. For example, to relate Footer to Note (and define that Note has one Footer):

Create or update models/footer.json:

{
  "name": "Footer",
  "base": "PersistedModel",
  "properties": {
    "content": {
      "type": "string",
      "required": true
    }
  },
  "relations": {
    "note": {
      "type": "belongsTo",
      "model": "Note",
      "foreignKey": "noteId"
    "note": {
      "type": "belongsTo",
      "model": "Note",
      "foreignKey": "noteId"
    }
  },
  "casbin": {
    "policies": [
      "$authenticated,*,*,allow",
      "$everyone,*,read,allow",
      "$owner,*,__get__note,allow"
    ]
  },
  "casbin": {
    "policies": [
      "$authenticated,*,*,allow",
      "$everyone,*,read,allow",
      "$owner,*,__get__note,allow"
    ]
  }
}

Create or update models/note.json: Create or update models/note.json:

{
  "name": "Note",
  "base": "PersistedModel",
  "properties": {
    "title": {
      "type": "string",
      "required": true
    },
    "content": {
      "type": "string"
    "content": {
      "type": "string"
    }
  },
  "relations": {
    "footer": {
      "type": "hasOne",
      "model": "Footer",
      "foreignKey": "noteId"
    }
  },
  "casbin": {
    "policies": [
      "$authenticated,*,*,allow",
      "$everyone,*,read,allow",
      "$owner,*,__get__footer,allow"
      "$authenticated,*,*,allow",
      "$everyone,*,read,allow",
      "$owner,*,__get__footer,allow"
    ]
  }
}

Run the following command to regenerate the models:

westack-go generate

This will establish the relationship where Footer belongs to Note and Note has one Footer, allowing CRUD operations to respect the relationship automatically.


Building APIs

Creating a New API Endpoint

  1. Define the Model Create a JSON file in the models/ directory and define your data structure.

  2. Generate the Go Struct Run the following command to generate the corresponding Go file:

    westack-go generate
  3. Extend the Model If needed, extend the generated Go struct file for additional functionality.

  4. Adding Custom Logic Use BindRemoteOperationWithOptions to add new functionality or routes for an existing model.

Example:

package boot

import (
    "log"
    "github.com/fredyk/westack-go/v2/model"
    "github.com/fredyk/westack-go/v2/westack"
)

func SetupServer(app *westack.WeStack) {
    NoteModel, err := app.FindModel("Note")
    if err != nil {
        log.Fatalf("Error finding model: %v", err)
    }

    model.BindRemoteOperationWithOptions(NoteModel, CustomHandler, model.RemoteOptions().
        WithName("customEndpoint").
        WithPath("/notes/custom").
        WithContentType("application/json"))
}

type CustomInput struct {
    Field1 string `json:"field1"`
    Field2 int    `json:"field2"`
}

type CustomOutput struct {
    Message string `json:"message"`
    Status  string `json:"status"`
}

func CustomHandler(input CustomInput) (CustomOutput, error) {
    return CustomOutput{
        Message: "This is a custom endpoint.",
        Status:  "success",
    }, nil
}

Advanced Features

Swagger Integration

westack-go automatically generates Swagger documentation for your APIs. The Swagger UI is available at:

  • /swagger: Interactive API documentation.
  • /swagger/doc.json: The OpenAPI specification in JSON format.

Filters in westack-go

Overview

Filters in westack-go allow developers to:

  • Query, sort, paginate, and limit data retrieved from models.
  • Build flexible APIs that support custom data slices without hardcoding query logic.

Automatic Fields

westack-go automatically manages the following fields:

  • created: Added when a record is created.
  • modified: Updated whenever a record is modified.

Use Cases

Filters can be applied to standard CRUD endpoints, such as GET /notes, to refine the data returned.

Syntax and Structure

Filters are specified as JSON objects within the filter query parameter. Spaces within filter values should be replaced with the + character to ensure proper parsing by the API.

1. Filtering by Fields

You can filter records based on specific field values using the following format:

GET /notes?filter={"where":{"field":"value"}}

Example:

GET /notes?filter={"where":{"title":"Meeting"}}

This retrieves all Note records where the title field equals Meeting.

2. Advanced Conditions

For more complex filtering, you can use comparison operators:

  • $gt (greater than)
  • $gte (greater than or equal to)
  • $lt (less than)
  • $lte (less than or equal to)
  • $ne (not equal to)
  • $in (in array)
  • $regex (regular expression)

Example:

GET /notes?filter={"where":{"content":{"$regex":".*important.*"}}}

This retrieves all Note records where the content field contains the substring "important".

3. Sorting

You can sort records by one or more fields using the order parameter:

GET /notes?filter={"order":["field+ASC"]}

Example:

GET /notes?filter={"order":["title+ASC"]}

This retrieves all Note records sorted by the title field in ascending order.

4. Pagination

To limit the number of results returned and implement pagination, use the limit and skip parameters:

  • limit: Specifies the maximum number of records to return.
  • skip: Skips the specified number of records before returning results.

Example:

GET /notes?filter={"limit":10,"skip":20}

This retrieves 10 Note records starting from the 21st record.

5. Field Selection

To retrieve only specific fields from a record, use the fields parameter:

GET /notes?filter={"fields":{"field1":true,"field2":false}}

Example:

GET /notes?filter={"fields":{"title":true,"content":false}}

This retrieves only the title field and excludes the content field for all Note records.

Combining Filters

Filters can be combined to build complex queries:

GET /notes?filter={"where":{"title":"Meeting"},"order":["title+DESC"],"limit":5}

This retrieves up to 5 Note records where the title is "Meeting", sorted in descending order by title.

Examples in Practice

Example 1: Basic Filtering

Retrieve all notes where content contains "urgent":

GET /notes?filter={"where":{"content":{"$regex":".*urgent.*"}}}

Example 2: Pagination and Sorting

Retrieve the first 10 notes sorted by created in descending order:

GET /notes?filter={"order":["created+DESC"],"limit":10}

Example 3: Advanced Pagination

Skip the first 5 notes and retrieve the next 15 notes:

GET /notes?filter={"limit":15,"skip":5}

Example 4: Complex Filtering

Retrieve all notes where title is "Meeting" and content does not contain "canceled":

GET /notes?filter={"where":{"title":"Meeting","content":{"$not":{"$regex":".*canceled.*"}}}}

Including Relations

To include related models, use the include parameter:

GET /notes?filter={"include":[{"relation":"footer"}]}

This retrieves Note records along with their related Footer records.

Including Relations with Filtering

You can include related models and apply additional filters simultaneously. For example:

GET /notes?filter={"where":{"title":"Meeting"},"include":[{"relation":"footer"}]}

This retrieves Note records where the title is "Meeting" and includes the related Footer records.

Limitations and Considerations

Performance: Complex filters might impact query performance, especially with large datasets.

Filters are a powerful feature of westack-go, making it easy to build flexible, queryable APIs. For further customization or troubleshooting, consult the source code or westack-go examples.


Change Log

  • v2.0.1-alpha

    • Now the DELETE /:id endpoint returns a wst.DeleteResult schema object instead of an empty response
    • Now the GET /count endpoint returns a wst.CountResult schema object instead of a root integer
  • v1.6.14

  • v1.6.0

  • v1.6.0

    • Added parameter strictSingleRelatedDocumentCheck in config.json, defaults to truein new projects, and false in existing ones.
    • "hasOne" and "belongsTo" relations are now checked after fetching documents from Mongo. If strictSingleRelatedDocumentCheck is true and the relation returns more than 1 document, an error is thrown. Otherwise, only the first document is used and a warning is logged.
    • Breaking changes:
      • model.Build() requires now parameter sameLevelCache *buildCache to be passed in. Can be generated with model.NewBuildCache()
      • model.Build() returns now error as second value, in addition to the instance. So it is now func (loadedModel *Model) Build(data wst.M, sameLevelCache *buildCache, baseContext *EventContext) (Instance, error)
    • Added parameter strictSingleRelatedDocumentCheck in config.json, defaults to truein new projects, and false in existing ones.
    • "hasOne" and "belongsTo" relations are now checked after fetching documents from Mongo. If strictSingleRelatedDocumentCheck is true and the relation returns more than 1 document, an error is thrown. Otherwise, only the first document is used and a warning is logged.
    • Breaking changes:
      • model.Build() requires now parameter sameLevelCache *buildCache to be passed in. Can be generated with model.NewBuildCache()
      • model.Build() returns now error as second value, in addition to the instance. So it is now func (loadedModel *Model) Build(data wst.M, sameLevelCache *buildCache, baseContext *EventContext) (Instance, error)
  • v1.5.48

  • v1.5.48

    • Breaking change: environment variables WST_ADMIN_USERNAME and WST_ADMIN_PWD are required to start the server
    • Breaking change: environment variables WST_ADMIN_USERNAME and WST_ADMIN_PWD are required to start the server

Contribute

Write to westack.team@gmail.com if you want to contribute to the project: D

You are also welcome on our official Discord

And of course... create as many pull requests as you want!

About

Strongly opinionated framework which allows you to quickly setup a REST API server in a few minutes

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 5