Skip to content

Commit

Permalink
switch to generics & libopenapi
Browse files Browse the repository at this point in the history
  • Loading branch information
fcjr committed Dec 31, 2024
1 parent d4eeaaf commit 7aa6a91
Showing 12 changed files with 505 additions and 340 deletions.
61 changes: 27 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@

<p align="center">
<img src="assets/logo.svg" alt="ShiftAPI Logo">
<img src="assets/logo.svg" alt="ShiftAPI Logo">
</p>

# ShiftAPI

Quickly write RESTful APIs in go with automatic openapi schema generation.

Inspired by the simplicity of [FastAPI](https://github.com/tiangolo/fastapi).

<!-- [![GitHub release (latest by date)][release-img]][release] -->
[![GolangCI][golangci-lint-img]][golangci-lint]
[![Go Report Card][report-card-img]][report-card]

## NOTE: THIS IS AN EXPERIMENT

This project is highly experimental -- the API is likely to change (currently only _basic_ post requests are even implemented).
This is **in no way production ready**.

This project was inspired by the simplicity of [FastAPI](https://github.com/tiangolo/fastapi).

Due to limitations of typing in go this library will probably not be production ready pre go 1.18 as handlers must be passed as `interface{}`s and validated at _runtime_ (Scary I know! 😱). Once generics hit I hope to rewrite the handler implementation to restore compile time type checking & safety.

## Installation

```sh
@@ -32,47 +25,47 @@ go get github.com/fcjr/shiftapi
package main

import (
"log"
"net/http"
"log"
"net/http"

"github.com/fcjr/shiftapi"
"github.com/fcjr/shiftapi"
)

type Person struct {
Name string `json:"name"`
Name string `json:"name"`
}

type Greeting struct {
Hello string `json:"hello"`
Hello string `json:"hello"`
}

// This is your http handler!
// ShiftAPI is responsible for marshalling the request body
// and marshalling the return value.
func greeter(p *Person) (*Greeting, *shiftapi.Error) {
return &Greeting{
Hello: p.Name,
}, nil
return &Greeting{
Hello: p.Name,
}, nil
}

func main() {

api := shiftapi.New(&shiftapi.Params{
SchemaInfo: &shiftapi.SchemaParams{
Title: "Greeter Demo API",
},
})

err := api.POST("/greet", greeter, http.StatusOK, &shiftapi.HandlerOpts{
Summary: "Greeter Method",
Description: "It greets you by name.",
})
if err != nil {
log.Fatal(err)
}

log.Fatal(api.Serve())
// redoc will be served at http://localhost:8080/docs
api := shiftapi.New(&shiftapi.Params{
SchemaInfo: &shiftapi.SchemaParams{
Title: "Greeter Demo API",
},
})

err := api.POST("/greet", greeter, http.StatusOK, &shiftapi.HandlerOpts{
Summary: "Greeter Method",
Description: "It greets you by name.",
})
if err != nil {
log.Fatal(err)
}

log.Fatal(api.Serve())
// redoc will be served at http://localhost:8080/docs
}
```

148 changes: 0 additions & 148 deletions app.go

This file was deleted.

10 changes: 5 additions & 5 deletions docs.go
Original file line number Diff line number Diff line change
@@ -27,11 +27,11 @@ const redocTemplate string = `<!DOCTYPE html>
`

type redocData struct {
Title string
FaviconURL string
RedocURL string
SpecURL string
EnableGoogleFonts bool
Title string
FaviconURL string
RedocURL string
SpecURL string
// EnableGoogleFonts bool
}

func genRedocHTML(data redocData, out io.Writer) error {
6 changes: 0 additions & 6 deletions error.go

This file was deleted.

47 changes: 13 additions & 34 deletions examples/greeter/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package main

import (
"encoding/json"
"context"
"errors"
"log"
"net/http"

@@ -16,46 +17,24 @@ type Greeting struct {
Hello string `json:"hello"`
}

func BadRequestError(code, msg string) *shiftapi.Error {
type err struct {
Code string `json:"code"`
Message string `json:"message"`
}
b, _ := json.Marshal(&err{
Code: code,
Message: msg,
})
return &shiftapi.Error{
Code: http.StatusBadRequest,
Body: b,
}
}

func greeter(p *Person) (*Greeting, *shiftapi.Error) {
if p.Name != "frank" {
return nil, BadRequestError("wrong_name", "I only greet frank.")
func greet(ctx context.Context, headers http.Header, person *Person) (*Greeting, error) {
if person.Name != "frank" {
return nil, errors.New("wrong name, I only greet frank")
}
return &Greeting{
Hello: p.Name,
Hello: person.Name,
}, nil
}

func main() {
ctx := context.Background()
server := shiftapi.New(ctx, shiftapi.WithInfo(shiftapi.Info{
Title: "Geeter Demo API",
}))

api := shiftapi.New(&shiftapi.Params{
SchemaInfo: &shiftapi.SchemaParams{
Title: "Greeter Demo API",
},
})

err := api.POST("/greet", greeter, http.StatusOK, &shiftapi.HandlerOpts{
Summary: "Greeter Method",
Description: "It greets anyone named 'frank'",
})
if err != nil {
log.Fatal(err)
}
handleGreet := shiftapi.Post("/greet", greet)
server.Register(handleGreet)

log.Fatal(api.Serve())
log.Fatal(server.ListenAndServe(":8080"))
// redoc will be served at http://localhost:8080/docs
}
21 changes: 11 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
module github.com/fcjr/shiftapi

go 1.17
go 1.23.0

require (
github.com/getkin/kin-openapi v0.80.0
github.com/julienschmidt/httprouter v1.3.0
)
toolchain go1.23.3

require github.com/pb33f/libopenapi v0.18.7

require (
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/dprotaso/go-yit v0.0.0-20240618133044-5a0af90af097 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240815153524-6ea36470d1bd // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 7aa6a91

Please sign in to comment.