Categorygithub.com/etixlabs/jsh-api
modulepackage
0.0.0-20160919171653-0e6c03967b59
Repository: https://github.com/etixlabs/jsh-api.git
Documentation: pkg.go.dev

# README

JSH-API

GoDoc Build Status Go Report Card

A JSON API specification micro-service builder created on top of jsh, Goji, and context to handle the nitty gritty but predictable (un)wrapping, validating, preparing, and logging necessary for any JSON API written in Go. The rest (storage, and business logic) is up to you.

Setup

The easiest way to get started is like so:

import github.com/derekdowling/jsh-api

// implement jshapi/store.CRUD interface and add resource specific middleware via Goji
userStorage := &UserStorage{}
resource := jshapi.NewCRUDResource("user", userStorage)
resource.UseC(yourUserMiddleware)

// setup a logger, your shiny new API, and give it a resource
logger := log.New(os.Stderr, "<yourapi>: ", log.LstdFlags)
api := jshapi.Default("<prefix>", true, logger)
api.Add(resource)

// launch your api
http.ListenAndServe("localhost:8000", api)

For a completely custom setup:

import github.com/derekdowling/jsh-api

// manually setup your API
api := jshapi.New("<prefix>")

// add a custom send handler
jshapi.SendHandler = func(c context.Context, w http.ResponseWriter, r *http.Request, sendable jsh.Sendable) {
    // do some custom logging, or manipulation
    jsh.Send(w, r, sendable)
}

// add top level Goji Middleware
api.UseC(yourTopLevelAPIMiddleware)

http.ListenAndServe("localhost:8000", api)

Feature Overview

There are a few things you should know about JSHAPI. First, this project is maintained with emphasis on these two guiding principles:

  • reduce JSONAPI boilerplate in your code as much as possible
  • keep separation of concerns in mind, let developers decide and customize as much as possible

The other major point is that this project uses a small set of storage interfaces that make handling API actions endpoint simple and consistent. In each of the following examples, these storage interfaces are utilized. For more information about how these work, see the Storage Example.

Simple Default CRUD Implementation

Quickly build resource APIs for:

  • POST /resources
  • GET /resources
  • GET /resources/:id
  • DELETE /resources/:id
  • PATCH /resources/:id
resourceStorage := &ResourceStorage{}
resource := jshapi.NewCRUDResource("resources", resourceStorage)

Relationships

Routing for relationships too:

  • GET /resources/:id/relationships/otherResource[s]
  • GET /resources/:id/otherResource[s]
resourceStorage := &ResourceStorage{}
resource := jshapi.NewResource("resources", resourceStorage)
resource.ToOne("foo", fooToOneStorage)
resource.ToMany("bar", barToManyStorage)

Custom Actions

  • GET /resources/:id/
resourceStorage := &ResourceStorage{}
resource := jshapi.NewResource("resources", resourceStorage)
resource.Action("reset", resetAction)

Other Features

  • Default Request, Response, and 5XX Auto-Logging

Working With Storage Interfaces

Below is a basic example of how one might implement parts of a CRUD Storage interface for a basic user resource using jsh for Save and Update. This should give you a pretty good idea of how easy it is to implement the Storage driver with jsh.

type User struct {
    ID string
    Name string `json:"name"`
}

func Save(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) {
    user := &User{}
    err := object.Unmarshal("user", user)
    if err != nil {
        return err
    }

    // generate your id, however you choose
    user.ID = "1234"

    err := object.Marshal(user)
    if err != nil {
        return nil, err
    }

    // do save logic
    return object, nil
}

func Update(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) {
    user := &User{}
    err := object.Unmarshal("user", user)
    if err != nil {
        return err
    }

    user.Name = "NewName"
    
    err := object.Marshal(user)
    if err != nil {
        return nil, err
    }

    // perform patch
    return object, nil
}

# Functions

Default builds a new top-level API with a few out of the box additions to get people started without needing to add a lot of extra functionality.
DefaultSender is the default sender that will log 5XX errors that it encounters in the process of sending a response.
New initializes a new top level API Resource without doing any additional setup.
NewCRUDResource generates a resource.
NewMockResource builds a mock API endpoint that can perform basic CRUD actions: GET /types POST /types GET /types/:id DELETE /types/:id PATCH /types/:id Will return objects and lists based upon the sampleObject that is specified here in the constructor.
NewResource is a resource constructor that makes no assumptions about routes that you'd like to implement, but still provides some basic utilities for managing routes and handling API calls.

# Constants

ToMany signifies a one to many relationship.
ToOne signifies a one to one relationship.

# Variables

EnableClientGeneratedIDs is an option that allows consumers to allow for client generated IDs.
SendHandler allows the customization of how API responses are sent and logged.

# Structs

API is used to direct HTTP requests to resources.
MockStorage allows you to mock out APIs really easily.
Resource holds the necessary state for creating a REST API endpoint for a given resource type.
Route represents a resource route.

# Type aliases

MockToManyStorage allows you to mock out APIs to-many relationships really easily.
MockToOneStorage allows you to mock out APIs to-one relationships really easily.
Relationship helps define the relationship between two resources.
Sender is a function type definition that allows consumers to customize how they send and log API responses.